Give CoinJar a consistent look with WPF styles: setters, TargetType, keyed and implicit styles, BasedOn inheritance, DataGrid styles and how local values win.

Bishal Biswas
Self Learner
What a style is
In Resources and Resource Dictionaries you named single values, such as a brush or a margin. But a button’s look is many values together: background, foreground, padding, font weight, cursor.
A style is a named bundle of property values that you can apply to many elements at once. If you have used CSS on the web, the idea is similar: describe the look once, apply it everywhere.
Without a style, every CoinJar button repeats the same five attributes:
<Button Content="Save" Background="#2E7D32" Foreground="White" Padding="14,6" FontWeight="SemiBold" Cursor="Hand" />
<Button Content="Add" Background="#2E7D32" Foreground="White" Padding="14,6" FontWeight="SemiBold" Cursor="Hand" />
<Button Content="Export" Background="#2E7D32" Foreground="White" Padding="14,6" FontWeight="SemiBold" Cursor="Hand" />
With a style, each button only says what is different about it: its text.
Your first style
A Style lives in a resource dictionary. It has a TargetType and a list of Setter elements. Each setter names a Property and a Value:
<Window.Resources>
<Style x:Key="PrimaryButton" TargetType="Button">
<Setter Property="Background" Value="#2E7D32" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="14,6" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Cursor" Value="Hand" />
</Style>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<Button Content="Save" Style="{StaticResource PrimaryButton}" />
<Button Content="Add" Style="{StaticResource PrimaryButton}" />
<Button Content="Export" Style="{StaticResource PrimaryButton}" />
</StackPanel>
TargetType="Button"says which kind of element the style is for. It also lets you writeBackgroundinstead of the long formButton.Background.- Each
Settersets one property. TheValueis written exactly like an attribute value in normal XAML. Style="{StaticResource PrimaryButton}"applies it.
Setters can only target dependency properties. A dependency property is a special kind of WPF property that supports styles, bindings, animation and resources. Nearly every property you set in XAML (Background, Margin, FontSize) is one. You will learn how they work in Dependency Properties.
Setter values that are not simple text
When a value is more than a short string, use property element syntax inside the setter, as you did for elements in XAML Syntax Basics:
<Style x:Key="HeaderBar" TargetType="Border">
<Setter Property="Padding" Value="16,10" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#2E7D32" Offset="0" />
<GradientStop Color="#43A047" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
Setter values can also be resources and bindings:
<Setter Property="Background" Value="{StaticResource AccentBrush}" />
<Setter Property="ToolTip" Value="{Binding Title}" />
Implicit styles: apply to every element of a type
Leave out the x:Key and the style becomes implicit. It applies automatically to every element of that TargetType in scope, with no Style="..." needed:
<Window.Resources>
<Style TargetType="TextBox">
<Setter Property="Padding" Value="6,4" />
<Setter Property="Margin" Value="0,2,0,12" />
</Style>
</Window.Resources>
<!-- Both pick up the style automatically -->
<TextBox Text="{Binding Title}" />
<TextBox Text="{Binding Amount}" />
Under the hood, an implicit style’s key is the type itself. That is why you will sometimes see it written as {x:Type TextBox}.
Where you put it decides how far it reaches, following the same lookup rules as other resources:
- in a panel’s
Resources: only inside that panel - in
Window.Resources: that window - in
App.xamlor a merged dictionary: the whole app
An element can opt out
Set Style="{x:Null}" on an element to skip an implicit style. This is rarely needed, but handy for a one off.
Only the exact type
An implicit style for Button does not apply to a ToggleButton or RepeatButton, even though they are related. The match is on the exact type. Give those types their own style, usually BasedOn the button one (see below).
BasedOn: building styles from styles
Styles can inherit from another style with BasedOn, just as a C# class can use inheritance. The new style gets all the setters of the base and can add or override some:
<Style x:Key="BaseButton" TargetType="Button">
<Setter Property="Padding" Value="14,6" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Cursor" Value="Hand" />
</Style>
<Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource BaseButton}">
<Setter Property="Background" Value="{StaticResource AccentBrush}" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style x:Key="DangerButton" TargetType="Button" BasedOn="{StaticResource BaseButton}">
<Setter Property="Background" Value="{StaticResource DangerBrush}" />
<Setter Property="Foreground" Value="White" />
</Style>
To extend an implicit style, refer to it by its type key:
<Style x:Key="SmallTextBox" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Width" Value="90" />
</Style>
This is a very common pattern. Without BasedOn, a keyed style replaces the implicit one completely, and you lose all of its setters.
Who wins: local values beat styles
If an element sets a property directly and its style sets the same property, the local value wins:
<!-- The style says green, but this button is purple -->
<Button Content="Special" Style="{StaticResource PrimaryButton}" Background="Purple" />
WPF has a fixed order of who wins when several things set the same property. It is called dependency property value precedence. A simplified version, from strongest to weakest:
1. Animations
2. Local value (set in XAML or code on the element)
3. Style triggers
4. Style setters
5. The control's built in theme style
6. Value inherited from a parent (for example FontSize)
7. Default value
The practical lesson: if a style seems to “not work”, check whether the element also sets that property directly. This matters even more in the next lesson, Triggers, because a local value also blocks style triggers.
Styling attached properties and events
Setters work with attached properties too. Write the owner type and the property name. Here we move the error template from Input Validation into the implicit TextBox style, so every text box gets it:
<Style TargetType="TextBox">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource FriendlyError}" />
</Style>
An EventSetter attaches an event handler from a style. The handler goes in the code behind of the file that holds the style:
<Style TargetType="TextBox">
<EventSetter Event="GotKeyboardFocus" Handler="TextBox_SelectAll" />
</Style>
private void TextBox_SelectAll(object sender, RoutedEventArgs e)
=> ((TextBox)sender).SelectAll();
Styling the parts of a DataGrid
Complex controls offer extra style properties for their pieces. For the CoinJar grid from The DataGrid Control:
<DataGrid>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Padding" Value="8,6" />
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Amount" Binding="{Binding Amount, StringFormat=C}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
ColumnHeaderStylestyles the header cells.RowStylestyles eachDataGridRow, andCellStyleeachDataGridCell.- A text column’s
ElementStylestyles theTextBlockshown in normal mode, andEditingElementStyletheTextBoxused when editing.
Lists have a similar property called ItemContainerStyle, which styles each ListBoxItem wrapper around your data template.
Putting styles in their own file
Styles grow quickly. Move them to Themes/Styles.xaml and merge it in App.xaml after your colours, because the styles use the colour resources:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Colors.xaml" />
<ResourceDictionary Source="Themes/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
What styles cannot do
A style can only set properties that the control already has. It cannot change the control’s basic shape. For example, the standard Button draws its own border and hover colour, and a Background setter may seem to be ignored when the mouse is over it. To change the shape and the hover look you replace the control’s template, which you will do in Control Templates.
Common mistakes
- A keyed style hides the implicit one. Add
BasedOn="{StaticResource {x:Type Button}}". - The style is ignored on one element. That element sets the same property locally. Remove the local value.
- “Cannot find resource”. The style uses a brush that is merged later, or not at all. Merge colours before styles.
- Wrong TargetType. A
Style TargetType="Button"applied to aTextBlockthrows an error when the window loads. - A style for TextBlock in App.xaml changes too much. An implicit
TextBlockstyle inApp.xamlalso reaches text drawn inside other controls, such as buttons and combo boxes. Prefer a keyed style forTextBlock, or keep the implicit one in a window or panel.
Quick recap
- A style is a reusable bundle of
Setterelements for oneTargetType. - Keyed styles are applied with
Style="{StaticResource Key}". Implicit styles (no key) apply to every element of that exact type in scope. BasedOnlets one style extend another. Use{StaticResource {x:Type Button}}to extend an implicit style.- Local values beat style setters, following dependency property value precedence.
- Setters work with attached properties, and
EventSetterhooks up events. DataGridhasColumnHeaderStyle,RowStyle,CellStyleandElementStyle. Lists haveItemContainerStyle.
<!-- Themes/Styles.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Shared button basics -->
<Style x:Key="BaseButton" TargetType="Button">
<Setter Property="Padding" Value="14,6" />
<Setter Property="Margin" Value="0,0,8,0" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Cursor" Value="Hand" />
</Style>
<!-- Implicit: every Button gets the basics -->
<Style TargetType="Button" BasedOn="{StaticResource BaseButton}" />
<Style x:Key="PrimaryButton" TargetType="Button" BasedOn="{StaticResource BaseButton}">
<Setter Property="Background" Value="{StaticResource AccentBrush}" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style x:Key="DangerButton" TargetType="Button" BasedOn="{StaticResource BaseButton}">
<Setter Property="Background" Value="{StaticResource DangerBrush}" />
<Setter Property="Foreground" Value="White" />
</Style>
<!-- Implicit TextBox style for every form field -->
<Style TargetType="TextBox">
<Setter Property="Padding" Value="6,4" />
<Setter Property="Margin" Value="0,2,0,12" />
</Style>
<!-- Keyed TextBlock styles, so text inside other controls is not affected -->
<Style x:Key="PageTitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="22" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="{StaticResource AccentBrush}" />
<Setter Property="Margin" Value="0,0,0,12" />
</Style>
<Style x:Key="FieldLabel" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource MutedTextBrush}" />
</Style>
<!-- A card is just a styled Border -->
<Style x:Key="Card" TargetType="Border">
<Setter Property="Background" Value="{StaticResource CardBrush}" />
<Setter Property="Padding" Value="16,12" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Margin" Value="0,0,0,12" />
</Style>
</ResourceDictionary>
<!-- Themes/Colors.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="AccentBrush" Color="#2E7D32" />
<SolidColorBrush x:Key="DangerBrush" Color="#C62828" />
<SolidColorBrush x:Key="PageBrush" Color="#F3F5F7" />
<SolidColorBrush x:Key="CardBrush" Color="#FFFFFF" />
<SolidColorBrush x:Key="MutedTextBrush" Color="#6B7280" />
</ResourceDictionary>
<!-- App.xaml -->
<Application x:Class="CoinJar.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Colours first, because the styles use them -->
<ResourceDictionary Source="Themes/Colors.xaml" />
<ResourceDictionary Source="Themes/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
<!-- MainWindow.xaml -->
<Window x:Class="CoinJar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CoinJar" Height="420" Width="460"
Background="{StaticResource PageBrush}">
<StackPanel Margin="16">
<TextBlock Text="Add expense" Style="{StaticResource PageTitle}" />
<Border Style="{StaticResource Card}">
<StackPanel>
<!-- The TextBoxes pick up the implicit style -->
<TextBlock Text="Title" Style="{StaticResource FieldLabel}" />
<TextBox Text="Groceries" />
<TextBlock Text="Amount" Style="{StaticResource FieldLabel}" />
<TextBox Text="42.80" />
<!-- A local value beats the style's Margin -->
<TextBlock Text="Notes" Style="{StaticResource FieldLabel}" />
<TextBox Text="Weekly shop" Margin="0,2,0,0" />
</StackPanel>
</Border>
<StackPanel Orientation="Horizontal">
<Button Content="Save" Style="{StaticResource PrimaryButton}" />
<Button Content="Delete" Style="{StaticResource DangerButton}" />
<!-- Implicit style: basics only -->
<Button Content="Cancel" />
</StackPanel>
</StackPanel>
</Window>
Was this explanation helpful?
Your feedback helps us improve the content for everyone.
Thank you for your feedback!
We appreciate you helping us improve.
What could we improve? (Optional)
Read aloud
Ready