Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Passing Custom-Properties into Styled/Templated Control with triggers

0.00/5 (No votes)
30 Aug 2013 1  
Simple way to pass custom(Attached) properties into a style-based control-template with triggers

 

Introduction

A few days ago, one of my colleagues came to me with a question - 'What is the easiest way to pass special properties into a templated control with triggers?' Later he came back with a solution he found on the internet, once saw it, I thought I could do better... 

Background 

For my test app I've choose to template a Button-Control in the well know pattern consist of a style with a template setter.

The Button will be Templated into an ellipse shaped Button, with the content (text). This content will be colored depending on Mouse-Over state. 

Using the code 

As it is implied from the test-app description we will need two special properties:

  1. Content(text)- Brush while Mouse-Over.
  2. Content(text)- Brush while Mouse-Not-Over. 

In WPF the term 'special'/'additional' properties usually directly translates to  'Attached-Properties'. So, I've added two of the matching types to my project (located inside the MainWindow's code-behind partial class , for simplicity. 

public static SolidColorBrush  GetMouseOverBrush(DependencyObject obj)
{
    return (SolidColorBrush )obj.GetValue(MouseOverBrushProperty);
}

public static void SetMouseOverBrush(DependencyObject obj, SolidColorBrush  value)
{
    obj.SetValue(MouseOverBrushProperty, value);
}

// Using a DependencyProperty as the backing store for MouseOverBrush.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty MouseOverBrushProperty =
    DependencyProperty.RegisterAttached("MouseOverBrush", 
    typeof(SolidColorBrush ), typeof(MainWindow), new PropertyMetadata(new SolidColorBrush(Colors.Green)));

public static SolidColorBrush  GetNotMouseOverBrush(DependencyObject obj)
{
    return (SolidColorBrush )obj.GetValue(NotMouseOverBrushProperty);
}

public static void SetNotMouseOverBrush(DependencyObject obj, SolidColorBrush  value)
{
    obj.SetValue(NotMouseOverBrushProperty, value);
}

// Using a DependencyProperty as the backing store for NotMouseOverBrush.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty NotMouseOverBrushProperty =
    DependencyProperty.RegisterAttached("NotMouseOverBrush", typeof(SolidColorBrush ), 
    typeof(MainWindow), new PropertyMetadata(new SolidColorBrush(Colors.LightBlue)));

These properties values can be set in three different layers:

  1. At the Attached-Property default value - as the property 'basic' default value (as seen above).
  2. At the style-level setters- acting as the Style's default value for these properties (as seen in derived style code).
  3. At the actual implementation of the style (styled-button on this case (second button))

After declaring those properties, the template (inside the style's 'template' property-setter), can use these properties in the following manner:

<Style x:Key="MyButtonStyle" TargetType="Button">
    <Setter Property="Template">
        <Setter.Value> 
            <ControlTemplate TargetType="Button">
                <Grid >
                    <Ellipse x:Name="ell"  Fill="Gray" Stroke="Red"/>
                    <ContentPresenter  x:Name="tb"  Content ="{TemplateBinding Content}" 
                      HorizontalAlignment="Center" VerticalAlignment="Center" 
                      TextElement.Foreground="{TemplateBinding  app:MainWindow.NotMouseOverBrush}" 
                      IsHitTestVisible="False"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger SourceName="ell" Property="IsMouseOver" Value="True">
                        <Setter Property="TextElement.Foreground" 
                          TargetName="tb" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, 
                          Path=(app:MainWindow.MouseOverBrush)}"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

And derived(based-on) styles can change them like this:

<Style x:Key="MySecondButtonStyle" BasedOn="{StaticResource MyButtonStyle}" TargetType="Button">
    <Setter Property="app:MainWindow.MouseOverBrush" Value="LightGreen"/>
    <Setter Property="app:MainWindow.NotMouseOverBrush" Value="Gold"/>
</Style>

Last, the Styled-Button implementation: 

<Button Grid.Row="0" Content="My Button Style" Style="{StaticResource MyButtonStyle}" />
<Button Grid.Row="1" Content="My Button Style Modified" Style="{StaticResource MyButtonStyle}" 
   app:MainWindow.MouseOverBrush="Yellow" app:MainWindow.NotMouseOverBrush="Pink"/>
<Button Grid.Row="2" Content="My second Button Style" Style="{StaticResource MySecondButtonStyle}"  />

That's it. hope You've found it useful.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here