I have a Templates.xaml file in my project which contains the ResourceDictionary of styles.
I want to bind Direction of a DropShadowEffect to MyNamespace.MyPage.LightDirection. Currently I did this:
// MyPage.xaml.cs
/// <summary>
/// Direction of light source for shadows and gradients.
/// </summary>
public static double LightDirection => 45;
<!--Templates.xaml-->
<Style x:Key="ControlButtons" TargetType="{x:Type Button}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="4" Direction="{Binding LightDirection}" Color="Black" Opacity="0.5" BlurRadius="4" />
</Setter.Value>
</Setter>
<!--Other Setters-->
</Style>
<!--MyPage.xaml-->
<Page x:Name="MyPage"
DataContext="{Binding ElementName=MyPage}">
<!--MyPage as some other default attributes which I didn't write here-->
<Grid>
<Button x:Name="MyButton" Style="{StaticResource ControlButtons}">
My Button
</Button>
</Grid>
</Page>
It works. The shadow direction is set to LightDirection and program runs normally. However, while debugging, debugger shows a binding error:
Cannot find governing FrameworkElement or FrameworkContentElement for target element.
What is my mistake? How should I prevent this error?
I suggest making a constant
namespace MyNamespace
{
public class Constants
{
public static double LightDirection { get; set; }
}
}
XAML
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace">
<Style x:Key="ControlButtons" TargetType="{x:Type Button}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="4"
Direction="{x:Static local:Constants.LightDirection}"
Color="Black"
Opacity="0.5"
BlurRadius="4" />
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Or declare constant directly in XAML
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
xmlns:sys="clr-namespace:System;assembly=mscorlib" >
<sys:Double x:Key="LightDirection">45.0</sys:Double>
<Style x:Key="ControlButtons" TargetType="{x:Type Button}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="4"
Direction="{StaticResource LightDirection}"
Color="Black"
Opacity="0.5"
BlurRadius="4" />
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Related
In a WPF project, I have an 'ExtendedButton' UserControl which inherits from Button, and overrides the control template of the button to add things like a Border, allowing for easy addition of rounded corners. I like keeping this fairly generic so as it is, it still looks fairly plain.
In a project using the project above, in the App.xaml I have created a style with a custom key, and a target type of this 'ExtendedButton'. In a control in this project I have added an 'ExtendedButton' and set its style explicitly to the style mentioned above. It appears to have reverted to the style of the base of the 'ExtendedButton', a key symptom of this being setting width etc works, but the corner radius property, while recognised, is not applied.
I've tried setting the control template in my style, which correctly adds properties like the corner radius, but doesn't allow for any customisation in my control where the button is placed. No matter what I do it seems to revert to being a base button.
An MWE of the setup described above:
The button:
ExtendedButton.xaml:
<Button
x:Class="Win10Themables.Controls.ExtendedButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Win10Themables.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="buttonExtension"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Button.Resources>
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD" />
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070" />
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD" />
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1" />
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6" />
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B" />
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4" />
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5" />
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383" />
</Button.Resources>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ThemableFocusStyle}" />
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True" />
<Setter Property="Background" Value="{StaticResource Button.Static.Background}" />
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{Binding Path=CornerRadius, ElementName=buttonExtension}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="contentPresenter"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
ExtendedButton.xaml.cs:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Win10Themables.Controls
{
public partial class ExtendedButton : Button
{
public ExtendedButton()
{
InitializeComponent();
}
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius", typeof(CornerRadius), typeof(ExtendedButton), new PropertyMetadata(new CornerRadius(0)));
}
}
The style:
<Application
x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Win10Themables.Controls;assembly=Win10Themables"
xmlns:svgc="http://sharpvectors.codeplex.com/svgc/">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="RoundButtonStyle" TargetType="{x:Type controls:ExtendedButton}">
<Setter Property="Width" Value="20" />
<Setter Property="Height" Value="20" />
<Setter Property="CornerRadius" Value="10" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
A control with the button and style applied:
<UserControl
x:Class="UserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Win10Themables.Controls;assembly=Win10Themables"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<controls:ExtendedButton
Style="{StaticResource RoundButtonStyle}"
HorizontalAlignment="Left">
</controls:ExtendedButton>
</Grid>
</UserControl>
Your custom control has no default style and that's why it doesn't behave as you would expect.
ExtendedButton should be defined as a standalone class including a static constructor but no XAML file:
public class ExtendedButton : Button
{
static ExtendedButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExtendedButton),
new FrameworkPropertyMetadata(typeof(ExtendedButton)));
}
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
nameof(CornerRadius), typeof(CornerRadius), typeof(ExtendedButton), new PropertyMetadata(new CornerRadius(0)));
}
You should then define the default Style for it in a ResourceDictionary named generic.xaml located in a themes folder at the root of the project:
<Style TargetType="{x:Type local:ExtendedButton}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ThemableFocusStyle}" />
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
themes/generic.xaml is a naming convention. WPF will look for the default template in this exact location.
I have a custom template for my application Windows that I have built. It's in App.xaml
<Application.Resources>
<ResourceDictionary>
<Style x:Key="XWindow" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate >
<Border BorderThickness="3">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Direction="270" RenderingBias="Quality" ShadowDepth="0.5" Opacity="0.8" Color="#FF00B9FF"/>
</Border.Effect>
<Grid Background="White">
<local:ControlButtons Height="38" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Border BorderBrush="#99007CF7" BorderThickness="1"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
In my MainWindow.xaml I have applied this style like this (and it works) : <Window [...] Style="{DynamicResource XWindow }"
So the style is applied to the window. But when I put a control in the Window, I cannot see it or even select it. It's in the XAML code but even when I debug it's not on the Window.. Anyone has a clue ?
There's a screenshot :
XAML Problem
This is what it should normally do when I add a simple button : XAML Norrmal
As pointed out by #Clemens you have forgotten to add a ContentPresenter to your ControlTemplate. This is where the actual content of the window will be displayed.
You should also remember to put the ContentPresenter in an AdornedDecorator:
<Style x:Key="XWindow" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderThickness="3">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Direction="270" RenderingBias="Quality" ShadowDepth="0.5" Opacity="0.8" Color="#FF00B9FF"/>
</Border.Effect>
<Grid Background="White">
<local:ControlButtons Height="38" VerticalAlignment="Top" HorizontalAlignment="Right"/>
<Border BorderBrush="#99007CF7" BorderThickness="1">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The AdornedDecorator specifies the position of the AdornerLayer in the visual tree as stated on MSDN here: http://msdn.microsoft.com/en-us/library/system.windows.documents.adornerdecorator.aspx. You will for example need one if you intend to dipslay any validation errors in your window as validation errors are displayed on the adorner layer.
Edit: You should also set the TargetType property of the ControlTemplate:
<ControlTemplate TargetType="{x:Type Window}">
I am trying to apply validation error template and defining style in App.XAML.
<Application x:Class="MY.UI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BP.NES.UI"
>
<Application.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Grid>
<Border BorderBrush="#FFCB2E2E" BorderThickness="1" Background="#11FF0000" IsHitTestVisible="False" x:Name="errorBorder"/>
<AdornedElementPlaceholder x:Name="placeholder" />
<Popup AllowsTransparency="True" HorizontalAlignment="Right" HorizontalOffset="0" VerticalOffset="0" PopupAnimation="Fade" Placement="Left"
PlacementTarget="{Binding ElementName=errorBorder}" IsOpen="{Binding ElementName=placeholder, Path=AdornedElement.IsFocused, Mode=OneWay}">
<StackPanel Orientation="Horizontal">
<Polygon VerticalAlignment="Center" Points="0,4 4,0 4,8" Fill="#FFCB2E2E" Stretch="Fill" Stroke="#FFCB2E2E"
StrokeThickness="2" />
<Border Background="#FFCB2E2E" CornerRadius="4" Padding="4">
<TextBlock HorizontalAlignment="Center" Foreground="White" FontWeight="Bold" Margin="2,0,0,0"
Text="{Binding ElementName=placeholder, Path=AdornedElement.ToolTip, Mode=OneWay}" />
</Border>
</StackPanel>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
</Application>
Now in My Main Window I have following code:
<Window x:Class="MY.UI.View.MainWindow"
xmlns:local="clr-namespace:MY.UI.View"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
xmlns:vm="clr-namespace:MY.UI.ViewModel"
xmlns:rules="clr-namespace:MY.UI.Validations"
>
<Grid x:Name="MainGrid">
<TextBox Grid.Row="1"
x:Name="CellphoneNumberTextBox"
Grid.Column="1"
VerticalAlignment="Stretch"
Margin="10,0,0,10"
IsEnabled="{Binding ElementName=PereferenceRadio,Path=IsChecked}"
Text="{Binding CurrentEnrolmentDetail.CellNumber,NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True,UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</Grid>
</Window>
If I move style to Window.Resources, it just works fine ,but when I have this in App.XAML , does not work. Is this due to difference in namespace?
Set a style key in App.Xaml and use the key in your Textbox.
Example:
App.Xaml
<Style x:Key="HeaderStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="FontSize" Value="24" />
</Style>
Window:
<Window x:Class="WpfTutorialSamples.Styles.ExplicitStyleSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ExplicitStyleSample" Height="150" Width="300">
<StackPanel Margin="10">
<TextBlock>Header 1</TextBlock>
<TextBlock Style="{StaticResource HeaderStyle}">Header 2</TextBlock>
<TextBlock>Header 3</TextBlock>
</StackPanel>
</Window>
Or Override using BasedOn functionality
Try this if you don't have interest to create style key
App.Xaml
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<!-- ... -->
</Style>
I have declared my base styles in a ResourceDictionary in App.xaml, if i override them in a specific window like this, it usually works.
If all TextBox will remain similar thought application than use following in resource file.
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Background" Value="Blue"/>
</Style>
Else create key in App.xaml and use in the TextBox's where required as shown below:
<Style x:Key="TextboxBackgroundColor" TargetType="TextBox">
<Setter Property="Background" Value="Cyan"/>
</Style>
<TextBox x:Name="txtText" Style="{StaticResource TextboxBackgroundColor}" />
I've observed that when defining a custom button style inside a MergedDictionaries tag, the style is only applied on the second (and all others) instance of the control.
It does not happen when I omit the MergedDictionaries and proceeding ResourceDictionary tag.
Oddly, the Visual Studio (2015) designer shows my expected appearance - it's only at runtime that the error occurs.
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style x:Key="mybutton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource mybutton}" TargetType="{x:Type Button}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<Button Height="100" />
<Button Height="50" />
</StackPanel>
</Window>
Note this is a completely blank template other than the XAML above:
You're not using the MergedDictionaries right, according to this link:
https://msdn.microsoft.com/en-us/library/system.windows.resourcedictionary.mergeddictionaries.aspx?f=255&MSPPError=-2147217396
"Dictionaries are merged by adding a ResourceDictionary to the generic collection referenced by MergedDictionaries. A merged ResourceDictionary does not have resource elements defined within it in markup. Instead, the merged dictionary is a ResourceDictionary with no markup child elements defined (or with no elements added through code), but with a URI specified for Source. "
You can easily write the styles like this:
<Window x:Class="WpfApplication5.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<Style x:Key="mybutton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource mybutton}" TargetType="{x:Type Button}" />
</Window.Resources>
<StackPanel>
<Button Height="100" />
<Button Height="50" />
</StackPanel>
</Window>
I've search around here on SO and what not able to find a clear answer explaining how to setup a 'style resource'. In my case my dialog controls several buttons and lists, and other various controls which I want to set a generic theme/style for. Similar to how you would do using a CSS file in HTML.
For the sake of simplicity in this example I have a style i want to use across the board on all my buttons. However I would prefer not to contain all these style resources in the xaml of my UI layout. I would like to move the styles to a general xaml resource file which would contain just the styles, that way i could also easily reference them into other wpf dialogs throughout the tool.
How do I set this up to make use of a general resource file containing styles for the various controls in my tool? Then be able to reference and use that style in my xaml UI's.
Thank you
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="200">
<Window.Resources>
<Style TargetType="Button" x:Key="CoolButton" >
<Setter Property="Margin" Value="1,2,1,2"/>
<Setter Property="Background" Value="LightBlue"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="FontSize" Value="12" />
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="Lavender" BorderThickness="5" CornerRadius="6,0,6,0" x:Name="bd">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bd" Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Style="{StaticResource CoolButton}" Content="Button" Margin="2" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<Button Style="{StaticResource CoolButton}" Content="Button" Margin="2" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<CheckBox Margin="2" Content="Great"></CheckBox>
</StackPanel>
</Window>
On a side note why does this not work to use variables for colors in the resource style xaml?
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--COLORS-->
<Color x:Key="AccentColor">#CC4021</Color>
<Style TargetType="Button">
<Setter Property="Foreground" Value="{StaticResource AccentColor}"/>
</Style>
</ResourceDictionary>
There are a few steps to follow from making a instance-specific style a generic style.
Remove the Key. This will make the style to be used for every button:
<Style TargetType="Button">
Move it to a resource file, for example Default.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Style TargetType="Button">
...
</Style>
</ResourceDictionary>
Include a reference to the resource from a central point, for example the App.xaml, which will load the resources. The App.xaml will cause the styles to be used application-wide in just one go:
<Application x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Default.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>