Control themes
Control themes build upon styles to create switchable themes for controls. Unlike regular styles, which accumulate and cannot be removed once applied, a control theme can be replaced entirely. This makes control themes the right choice when you need to swap out a control's complete look for a specific instance or section of your UI.
Control themes are themselves styles, but with some important differences:
- Control themes don't have a selector: instead they have a
TargetTypeproperty which describes the control that they target - Control themes are stored in a
ResourceDictionaryinstead of aStylescollection - Control themes are assigned to a control by setting the
Themeproperty, usually using the{StaticResource}markup extension
Because control themes are based on styles, it is important to understand the Avalonia styling system first.
Control themes are typically applied to templated (lookless) controls, but they can actually be applied to any control. However, for non-templated controls, it is often more convenient to use standard styles instead.
Example: round button
The following example shows a simple Button theme which displays a button with an ellipse background with a 90's Geocities aesthetic:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ControlTheme x:Key="EllipseButton" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Ellipse Fill="{TemplateBinding Background}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
</Application.Resources>
</Application>
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Class="Sandbox.MainWindow">
<Button Theme="{StaticResource EllipseButton}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
Hello World!
</Button>
</Window>
Interaction in control themes
Like standard styles, control themes support nested styles which can be used to add interactions such as pointer-over and pressed states.
Example: round button hover state
Using nested styles we can make our button change color when the pointer is hovered over it:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ControlTheme x:Key="EllipseButton" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Ellipse Fill="{TemplateBinding Background}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</ControlTheme>
</Application.Resources>
</Application>
Control theme lookup
There are two ways in which a control theme can be found:
- If the control's
Themeproperty is set, then that control theme will be used; otherwise - Avalonia will search upwards through the logical tree for a
ControlThemeresource with anx:Keywhich matches the control's style key
If you're having trouble getting Avalonia to find your theme, make sure it's returning a style key which matches the x:Key and TargetType of your control theme.
In effect this means that you have two choices for how to define your control theme:
- If you want the control theme to apply to all instances of the control then use an
{x:Type}as the resource key. For example<ControlTheme x:Key="{x:Type Button}" TargetType="Button"> - If you want the control theme to be applied to selected instances of the control then use anything else as the resource key and look up this resource using
{StaticResource}. Commonly this key will be astring
Notice that this means that only a single control theme can be applied to a control at any one time.
Example: make all the buttons round
To apply the control theme to all buttons in the application, change the x:Key of the control theme to match the Button type.
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ControlTheme x:Key="{x:Type Button}" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Ellipse Fill="{TemplateBinding Background}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</ControlTheme>
</Application.Resources>
</Application>
TargetType
The ControlTheme.TargetType property specifies the type to which setter properties apply. If you don't specify a TargetType, you must qualify the properties in your Setter objects with a class name by using the syntax Property="ClassName.Property". For example, instead of setting Property to FontSize, you must set Property to TextBlock.FontSize or Control.FontSize.
See also
- ButtonCustomize sample with a
WinClassicButtonTheme - Control themes for built-in Avalonia controls:
- Styles
- Control template walkthrough