I have a standard style for my buttons but I want certain parts of the style to be configurable. e.g. I have a border appear when MouseOver is triggered for the button and I want the border colour to be configurable.
Following this article: http://www.thomaslevesque.com/2011/10/01/wpf-creating-parameterized-styles-with-attached-properties/ I thought I could use attached properties and TemplateBinding to achieve this.
I created the following attached property:
public static class ThemeProperties { public static Brush GetButtonBorderColour(DependencyObject obj) { return (Brush)obj.GetValue(ButtonBorderColourProperty); } public static void SetButtonBorderColour(DependencyObject obj, Brush value) { obj.SetValue(ButtonBorderColourProperty, value); } public static readonly DependencyProperty ButtonBorderColourProperty = DependencyProperty.RegisterAttached( "ButtonBorderColour", typeof(Brush), typeof(ThemeProperties), new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.Inherits)); }
I set the property like so:
<Button Style="{StaticResource RedButton}" local:ThemeProperties.ButtonBorderColour="#B20000"/>
And my style looks like this:
<Window.Resources> <Style x:Key="RedButton" TargetType="Button"> <Setter Property="OverridesDefaultStyle" Value="True"/> <Setter Property="Margin" Value="2"/> <Setter Property="FontFamily" Value="Tahoma"/> <Setter Property="FontSize" Value="11px"/> <Setter Property="FontWeight" Value="Bold"/> <Setter Property="Foreground" Value="White"/> <Setter Property="MinHeight" Value="25" /> <Setter Property="FocusVisualStyle" Value="{StaticResource MyFocusVisual}" /> <Setter Property="Background" > <Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" > <GradientStop Color="#FECCBF" Offset="0.2"/> <GradientStop Color="Red" Offset="0.85"/> <GradientStop Color="#FECCBF" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border Name="border" BorderThickness="1" Padding="4,2" BorderBrush="Transparent" CornerRadius="3" Background="{TemplateBinding Background}"> <Grid > <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Name="content"/> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="{TemplateBinding local:ThemeProperties.ButtonBorderColour}" /> <Setter Property="Foreground" Value="#B20000" /> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" > <Setter.Value> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" > <GradientStop Color="#FECCBF" Offset="0.35"/> <GradientStop Color="Red" Offset="0.95"/> <GradientStop Color="#FECCBF" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter TargetName="content" Property="RenderTransform" > <Setter.Value> <TranslateTransform Y="1.0" /> </Setter.Value> </Setter> </Trigger> <Trigger Property="IsDefaulted" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="#B20000" /> </Trigger> <Trigger Property="IsFocused" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="#B20000" /> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="border" Property="Opacity" Value="0.7" /> <Setter Property="Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources>
Where the key line is
<Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="border" Property="BorderBrush" Value="{TemplateBinding local:ThemeProperties.ButtonBorderColour}" /> <Setter Property="Foreground" Value="#B20000" /> </Trigger>
As far as I can see this should work but I get the following error during runtime on the above line:
Cannot convert the value in attribute 'Value' to object of type ''. Error at object 'System.Windows.Setter' in markup file
Have I done something incorrect here? I'm brand new to WPF and can't figure what's going wrong as the Type of the attached property is a Brush which is what I would expect the BorderBrush property of a Border to want.
Attached properties are a special kind of DependencyProperties. They allow you to attach a value to an object that does not know anything about this value. A good example for this concept are layout panels. Each layout panel needs different data to align its child elements.
To modify the visual tree generated by a template to reflect the control's property values, you must use template bindings. A template binding is a special type of data binding that allows you to reference the parent control, read its properties and apply their values. In some cases, you can use the values directly.
I think TemplateBinding is evaluated at compile time so you can't dynamically set a TemplateBinding in your Setter, try using Binding instead (see below).
<Setter TargetName="border" Property="BorderBrush" Value="{Binding Path=(local:ThemeProperties.ButtonBorderColour), RelativeSource={RelativeSource TemplatedParent}}"/>
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With