Understanding Styles and Resources in Windows and Uno Platform Applications

In my previous post I looked how the Windows UI 3 Figma file components and variants map to styles and visual states in Windows UI but glosses over some of the details of how resources and in particular, Styles, are defined in Windows UI. To cover this in more detail, in this post we’re going to drill into the default resources and styles that ship with both the Windows App Sdk and the Uno Platform.

The first thing to do is to locate the resources and styles that ship with the Windows App Sdk. These are conveniently located in the Microsoft.WindowsAppSdk package that can be found in your nuget packages folder (assuming you’ve created/loaded/restored a Windows App Sdk based application). Here’s the default location:

C:\Users\<usrename>.nuget\packages\microsoft.windowsappsdk

Side note – if you haven’t already checked it out, I would recommend setting up a Dev Drive, which you can do via Dev Home. After setting up a Dev Drive, you can move your nuget packages folder and cache to your Dev Drive (see instructions).

Within the the Microsoft.WindowsAppSdk package folder, you need to drill into a version folder, and then further into the lib folder, and then keep going until you find the generic.xaml file, located in the Themes folder. For example here’s the full path fo the 1.5.240627000 version of the Windows App Sdk.

1.5.240627000\lib\net6.0-windows10.0.18362.0\Microsoft.WinUI\Themes\generic.xaml

This file is a standard XAML (ie XML) file, so you can open it in any code editor. I typically open it in VS Code as I find it easier to search and navigate the file, since it provides some rudimentary XML color coding and the ability to expand/collapse XML elements.

ThemeResources

The generic.xaml file contains nothing more than a dictionary of resources, that are included by default in a Windows App Sdk application that uses Windows UI. The file begins with a set of three ThemeDictionaries, Default (aka Dark), HighContrast and Light.

As you can imagine, based on the names of these theme dictionaries, they define resources that should be used when the host system is set to dark (ie the default theme) or light (for applications), or if the high contrast accessibility option is enabled. Note that within your application, you can override the system theme by setting the RequestedTheme on elements within your application. This can be useful if you want to provide users with the option to specify a particular theme for your application but don’t forget to give them the option to revert back to the system theme.

The other interesting thing to note about theme resources is that they can be overridden at any level using a technique called light weight styling.

If you expand any of the theme dictionaries, you’ll see that it defines low level resources such as FontFamily, Min/Max Width/Height, Opacity, Thickness, Color and a lot of Brushes.

Following the themedictionaries there are some othe constant values resources that are defined. It’s not expected that these will be altered based on the theme and it doesn’t seem possible to override the resources and have the updated value take effect within the built in templates and styles.

Templates and Styles

The next section of the file defined DataTemplate and Style elements that control the look and feel of the built in Windows UI elements. For any given control type, there may be more than on Style defined but there can only be one default Style. Let’s take the Button control for example. There are multiple named (explicit) styles that have a TargetType of Button (or ButtonBase): AccentButtonStyle, EllipsisButton, …, and DefaultButtonStyle. It’s important to note that just because a style has been named DefaultButtonStyle, doesn’t make it the default style for a Button. It’s the definition of an implicit (unnamed) style that determines the default style for a Button.

<Style TargetType="Button" BasedOn="{StaticResource DefaultButtonStyle}" />

If we drill into the DefaultButtonStyle we can see that it starts with a number of property setters, that define the default property values set by the Style, and that can be overridden on an instance of the Button.

<Style x:Key="DefaultButtonStyle" TargetType="Button">
    <Setter Property="Background" Value="{ThemeResource ButtonBackground}" />
    <Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
    <Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" />
    <Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" />
    <Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />

Note that a lot of these properties are set to be a theme resource. This means that the property can either be updated by setting the property directly on the Button instance, or it be changed by redefining the theme resource.

One of the properties being set is the Template property, which defines a ControlTemplate.

<Style x:Key="DefaultButtonStyle" TargetType="Button">
    ...
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <ContentPresenter x:Name="ContentPresenter" ... >
                    <ContentPresenter.BackgroundTransition>
                        <BrushTransition Duration="0:0:0.083" />
                    </ContentPresenter.BackgroundTransition>
                    <VisualStateManager.VisualStateGroups>
                    ...
                    </VisualStateManager.VisualStateGroups>
                </ContentPresenter>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The Button ControlTemplate is relatively straightforward, being mostly made up of a ContentPresenter, essentially a host control for the Content defined on the Button instance. What’s been cropped in the above XAML for brievity is that the ContentPresenter has properties such as BorderThickness, BorderBrush and Background, which means that the Button ControlTemplate doesn’t need to define a separate element to draw a border or set the background on the content.

The other block of XAML that has been cropped in the above ControlTemplate are the VisualStateGroups for the Button. In fact there is only one VisualStateGroup, CommonStates, that defines the main interaction states of the Button, Normal, PointerOver, Pressed and Disabled. If you’re familiar with other flavours of XAML you might be wondering where the visual state group is for the focus states of the Button. These states aren’t defined by default because by default the Button style sets the UseSystemFocusVisuals property to true, meaning that focus visuals are applied by the system. If you want to provide custom focus visuals, set the UseSystemFocusVisuals property to false, and define a new visual state group with states, Focused, Unfocused and PointerFocused.

The following XAML shows the Normal state (which represents the default/rest state of the Button) and the PointerOver (ie when the mouse hovers over the control) state. The PointerOver state defines both some Storyboard and a property Setter. The first Storyboard (the others have been cropped for brevity) changes the Background on the ContentPresenter to the ButtonBackgroundPointerOver theme resource.

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="CommonStates">
        <VisualState x:Name="Normal" />
        <VisualState x:Name="PointerOver">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
                    <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPointerOver}" />
                </ObjectAnimationUsingKeyFrames>
                ...                
            </Storyboard>
            <VisualState.Setters>
                <Setter Target="ContentPresenter.(controls:AnimatedIcon.State)" Value="PointerOver" />
            </VisualState.Setters>
        </VisualState>

The important thing to note about how Styles are defined for controls is that it provides a way that you as a developer can override the look and feel of any of the controls. You can clone the existing Style and make any changes to either the property setters, or the ControlTemplate. You don’t need to clone the entire Style, instead you can base your new Style on the built-in Style (by setting the BasedOn attribute) and then only override the properties you want to modify.

<Style x:Key="CustomButtonStyle"
        TargetType="Button"
        BasedOn="{StaticResource DefaultButtonStyle}">
  <Setter Property="Padding"
          Value="40" />
</Style>

In summary the take away points are that if you want to understand the default control styles for Windows UI (and Uno Platform) you can inspect the generic.xaml file. You can also see from this file the additional styles that are available for a control. You can use this information to override theme resources, to make changes to the theme of your application. Lastly you can use these styles as the basis for your custom control styles.

1 thought on “Understanding Styles and Resources in Windows and Uno Platform Applications”

Leave a comment