I have started to work on WPF GUI programming again, and finally found out how to use nested styles in an application (pretty basic, I know, but I'm slowly getting to know the framework on my own
).
So, starting from the basics, to embed some global styles in the application, you can define an external ResourceDictionary (much like an external CSS stylesheet):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Colors -->
<Color x:Key="WhiteDark" A="255" R="238" G="240" B="245" />
<Color x:Key="WhiteBright" A="255" R="254" G="254" B="254" />
<!-- ... -->
<RadialGradientBrush x:Key="PaleBackgroundRadial" Center="0.8,0.8" GradientOrigin="0.8,0.8"
RadiusX="0.8" RadiusY="0.7">
<GradientStop Offset="0" Color="{StaticResource WhiteBright}" />
<GradientStop Offset="1" Color="{StaticResource WhiteDark}" />
</RadialGradientBrush>
<!-- ... -->
</ResourceDictionary>
In order to reference those resources in your code (using their x:Key name), you must embed the dictionary where it is needed. The simplest solution is to embed all styles at an application level:
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyResourceDictionary.xaml" />
<!-- other dictionaries -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Now, everywhere in your XAML code you'll be able to reference one of your styles, for instance:
<Rectangle Background="{StaticResource PaleBackgroundRadial}" />
Now, if you - like me - are used to web programming using CSS and HTML, you'll expect to be able to define a "class" with a custom style and then specify additional styles for all nested tags in an element of that class. You could for instance define a class for a menu and then define another (nested) style for all <b> bold tags inside it:
.menu {
width: 200px;
font-size: 0.8em;
background: red;
}
.menu b {
font-size: 1.2em;
color: blue;
}
The definition for the bold tag will only be applied to <b> elements inside the element with the "menu" class. The same can be done in XAML, albeit in a somewhat different way:
<Style TargetType="{x:Type Window}" x:Key="GlossyWindow">
<Setter Property="Background" Value="{StaticResource GlossyBackground}" />
<Setter Property="TextElement.FontFamily" Value="Segoe UI" />
<Setter Property="TextElement.Foreground" Value="White" />
<!-- Other setters, trigger, etc... -->
<!-- NESTED STYLES -->
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="3.0" />
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Rectangle />
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Menu}">
<!-- Setters, triggers, etc... -->
</Style>
</Style.Resources>
</Style>
The effect of the XAML code above is that if you define a Window's style as "GlossyWindow", automatically all buttons and menus contained in that same window will have the nested styles applied (in addition to other styles you might have explicitly set on each button or on each menu).
This allows you to efficiently mark only a root element as being styled and apply a custom appearance to all nested elements in the XML structure. However, using nested styles can get you some quite... nested XAML code: the code above is still readable, but obviously if you start using more nested levels you'll end with a huge XML hierarchy.
To clean the code up, define each single style individually and then declare the nested styles hierarchy by "basing" them on the previously defined templates (this is done by using the BasedOn attribute on your styles and referencing the styles above by their dictionary key):
<Style TargetType="{x:Type Button}" x:Key="GlossyButton">
<Setter Property="Margin" Value="3.0" />
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Rectangle />
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Menu}" x:Key="GlossyMenu">
<!-- Setters, triggers, etc... -->
</Style>
<Style TargetType="{x:Type Window}" x:Key="GlossyWindow">
<Setter Property="Background" Value="{StaticResource GlossyBackground}" />
<Setter Property="TextElement.FontFamily" Value="Segoe UI" />
<Setter Property="TextElement.Foreground" Value="White" />
<!-- Other setters, trigger, etc... -->
<!-- NESTED STYLES -->
<Style.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource GlossyButton}" />
<Style TargetType="{x:Type Menu}" BasedOn="{StaticResource GlossyMenu}" />
</Style.Resources>
</Style>