In my "Overview of Resources in WPF" article, I talked about Resources and how they are useful. However, there is one drawback to ‘Resources’ in WPF. The resource collection of each element contains individual objects and you need to refer to them separately to apply them.
- <Window.Resources>
- <SolidColorBrush x:Key="blueBrush" Color="CadetBlue" />
- <Thickness x:Key="padding">20</Thickness>
- </Window.Resources>
- <StackPanel>
- <Button Content="Blue Button" Background="{StaticResource blueBrush}" Padding="{StaticResource padding}" />
- </StackPanel>
Let’s say if you want to apply the same background and padding to all buttons throughout the application; there will be a lot of repetitive code. Also, developers will struggle to maintain the consistency throughout the application. To overcome this problem, WPF introduces ‘Styles’.
- <Window.Resources>
- <Style x:Key="btnStyle">
- <Setter Property="Button.Background"Value="CadetBlue"/><Setter Property="Button.Padding"Value="20"/>
- </Style>
- </Window.Resources>
- <StackPanel>
- <Button Content="Blue Button" Style="{StaticResource btnStyle}" />
- </StackPanel>
As you can see in the example, we have declared ‘Style’ in ‘Resource’ collection of ‘Window’ element. Then, we refer that style in ‘Button’ which reduces repetitive code in XAML. In the above example, it only reduces code by one line, but in real applications, since we want to set multiple properties, we will save multiple lines and we’ll have a cleaner and more elegant code.
Creating the Style Object in WPF
Basically, we create the ‘Style’ object as a resource object in a ‘Resource’ collection on an element. Just like any other resource, we can create ‘Style’ in any scope as per our requirement, i.e., local to the element, window level, application level or in Resource Dictionary.
- <Window.Resources>
- <SolidColorBrush x:Key="blueBrush" Color="CadetBlue" />
- <Thickness x:Key="padding">20</Thickness>
- <Style x:Key="btnStyle">
- <Setter Property="Button.Background"Value="CadetBlue"/><Setter Property="Button.Padding"Value="20"/>
- </Style>
- </Window.Resources>
In the above example, we created ‘Style’ object with other resource objects at window level resources. Just like any other resource, we defined the key attribute to refer to this ‘Style’ in an element. ‘Style’ is a collection of ‘Setter’ objects. In a ‘Setter’ object, we primarily set two attributes - ‘Property’ and ‘Value’. The ‘Property’ attribute refers to the attribute we are trying to set. ‘Value’ refers to the value that we want to assign to that property. The only limitation on setter object is that you can set only ‘Dependency Properties’. Other properties can’t be modified.
To set the ‘Property’ attribute of Setter object, you need to provide both, class name and property name. In the previous example, we specified the ‘Background’ property with the ‘Button’ class.
In the above example, we are trying to set the button background and padding to ‘CadetBlue’ and ‘20‘ respectively.
Now, after declaring the style, you must refer to it in an element. Every element has a ‘Style’ property inherited from ‘FrameworkElement’. Since single ‘Style’ object is nothing but a ‘Resource’, we can refer to it in any element just like normal resource using ‘StaticResource’ markup extension using the key of that ‘Style’.
- <Button Content="Blue Button" Style="{StaticResource btnStyle}"/>
We could use ‘DynamicResouce’ markup extension also but in this example, I am assuming we are not changing the resource.
Tip
Sometimes, you may want to set the same properties from a different class in same style object as follows.
We want to set ‘FontFamily’ property of ‘Button’ and ‘TextBlock’.
- <Style x:Key="btnConflictingStyles">
- <Setter Property="Button.FontFamily"Value="Times New Roman"/><Setter Property="Button.FontSize"Value="18"/><Setter Property="TextBlock.FontFamily"Value="Arial"/><Setter Property="TextBlock.FontSize"Value="40"/>
- </Style>
Our general thinking would be that different values will be applied to objects since properties are defined for separate objects. But since the ‘FontFamily‘ from Button and TextBlock references the same dependency property values, the application of this style will apply ‘FontFamily’ twice. The last applied property takes precedence.
Automatically Applying Styles by Type
So far, we have learned that to apply a ‘Style’, we must reference it from an element. Now, what if we want to apply one style to a lot of elements in one project (Consider 1000)? How inconvenient that would be? To overcome this, we can use ‘TargetType’ property of ‘Style’ object.
We set ‘TargetType’ property to the element we want to set ‘Style’ to. Then, that ‘Style’ would be applied to all the elements in the scope of that ‘Style’ unless overridden locally.
- <Window.Resources>
- <Style TargetType="Button">
- <Setter Property="Background"Value="Yellow"/>
- </Style>
- </Window.Resources>
As shown above, Style would be applied to all ‘Button’ objects within that window unless overridden locally.
Triggers
As the name suggests, ‘Triggers’ can be used to trigger changes in Property value when a particular event takes places.
For example, we may use a trigger to change the background of a button when a user hovers the mouse over it.
- <Style.Triggers>
- <Trigger Property="TextBox.IsMouseOver" Value="True">
- <Setter Property="TextBox.Background" Value="Aquamarine" />
- </Trigger>
- </Style.Triggers>
Like Setters collection, Styles also have a collection of Triggers. To create a ‘Trigger’ you create Trigger objects and set two attributes; i.e., ‘Property’ and ‘Value’. ‘Property’ describes the event that triggers the ‘Trigger’ and ‘Value’ describes the value for that event. Developers also declare ‘Setter’ object inside trigger that will change the property when an event in the ‘Trigger’ occurs. These are ‘Property Triggers’.
There are Data Triggers also, which basically detect the change in data and updates the property.
Following is an example of ‘DataTriggers’.
- <Style.Triggers>
- <DataTrigger Binding="{Binding ElementName=tb, Path=Text}" Value="">
- <Setter Property="Button.Background" Value="Red" />
- </DataTrigger>
- </Style.Triggers>
For data triggers, you use ‘DataTrigger’ markup instead of ‘Trigger’ markup. In our example, we are binding the trigger to the Text property of the TextBox. When the property value is empty, the background of the TextBox will be set to red. This technique can be used for creating required fields in your application.