KB Article K18542
Visible to All Users

How to implement the ThemeMananger theme support in custom controls

Description:

DevExpress provides a wide range of themes for its controls, as well as for many standard ones. The theme mechanism is very powerful and allows us to create visually rich applications and even allow users switch between different themes at runtime. However, what if we have custom controls that don't follow DevExpress themes?

Answer:

You can use one of the following approaches to make sure your controls look consistent with a currently selected theme.

1. Create default styles targeting your custom control for each theme used in your application. For example, the style below will be applied to your control when the DeepBlue theme is used:

XAML
<Style BasedOn="{StaticResource {x:Type local:CustomControl1}}" TargetType="{x:Type local:CustomControl1}" x:Key="{dxt:DefaultStyleThemeKey ThemeName=DeepBlue, Type={x:Type local:CustomControl1}, TypeInTargetAssembly={x:Type local:MainWindow}}"> <Setter Property="BorderBrush" Value="Blue" /> <Setter Property="Foreground" Value="Blue" /> <Setter Property="Background" Value="SkyBlue"/> </Style>

As you can see, this style can be based on a standard default style, so there is no need to repeat theme-independent settings for each theme.

Important Notes:
The TypeInTargetAssembly property should refer to a type from the assembly where your default styles are located. For example, if your custom controls are defined in one project and styles for these controls are in a separate assembly (project), set the TypeInTargetAssembly property to the type from the assembly where styles are located.

Just like all default styles, theme-dependent default styles should be placed into the Themes/Generic.xaml resource dictionary in your project. In addition, you will need to override the DefaultStyleKey property for your control:

C#
static CustomControl1(){ DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1))); }

If your custom control is defined in a separate assembly, it's necessary to mark this assembly with the DXThemeInfo attribute in the AssemblyInfo.cs file.

Pros:
You have complete control over your controls' appearance in various themes. If necessary, you can create different templates for each theme.
If no style for a specific theme can be found, the standard default style will be used.
Cons:
It is necessary to create custom styles for every theme/custom control separately. If you have many custom controls and are going to support all DevExpress themes, there will be plenty of work.
Since default styles are used, theme-specific appearance cannot be applied to controls only in a specific window or UserControl. You cannot apply such styles to controls without the overridden DefaultStyleKey property as well (e.g.,. to a specific Border element on a Login form).

2. Reference DevExpress resources (e.g. brushes) in a custom control's style and load these resources for a currently selected theme at runtime. To load resources, use the ThemeResource markup extension:

XAML
<Style TargetType="{x:Type local:CustomControl2}"> <Setter Property="Background" Value="{dxi:ThemeResource {dxt:FloatingContainerThemeKey ResourceKey=FloatingContainerBackground}}" /> <Setter Property="Foreground" Value="{dxi:ThemeResource {dxgt:GridColumnHeaderThemeKey ResourceKey=GroupHeaderCaptionForeground}}" /> </Style>

Pros:
Such a style can be used everywhere, not only in the Generic.xaml file. It is also possible to assign theme resources to a control directly, without a style.
Overriding the DefaultStyleKey property is not required.
Theme-specific resources are retrieved automatically. This means that you don't need to create styles for multiple themes.
You can specify a theme resource value manually by adding an item with a corresponding theme key to resources:

XAML
<SolidColorBrush x:Key="{dxt:FloatingContainerThemeKey ResourceKey=FloatingContainerBackground, ThemeName=MetropolisDark}" Color="Red" />

Therefore, your control can look different depending on the resources in a particular place in your application.

Cons:
You will need to find DevExpress resources for your requirements. For example, the FloatingContainerBackground resource can be used to specify the background for your control. However, if you wish to have a slightly different background, it will be necessary either to redefine the resource for the themes where you want a different background or use another theme key.
Certain resources may not be defined in all themes. You will need to define resources for such themes manually.
With this approach, you can make certain settings (such as the background) theme dependent. However, if you need more than that and want to create templates for each theme, you will still need to use approach #1.

Important Notes:
The ThemeResource markup extension can be also used for obtaining palette colors as described at How to: Use Palette Resources.

3. This approach is based on the previous one, but instead of using DevExpress resources, you will create your own resources with custom theme keys:

C#
public class ThemeResourcesThemeKeyExtension : ThemeKeyExtensionBase<ThemeResourcesThemeKeys> { // no need to override this if you are declaring resources in a DevExpress theme assembly public override Assembly Assembly { get { return TypeInTargetAssembly != null ? TypeInTargetAssembly.Assembly : GetType().Assembly; } } } public enum ThemeResourcesThemeKeys { Background, Foreground }
XAML
<SolidColorBrush x:Key="{local:ThemeResourcesThemeKey ResourceKey=Background}" Color="LightBlue" /> <SolidColorBrush x:Key="{local:ThemeResourcesThemeKey ResourceKey=Foreground}" Color="DarkBlue" /> <SolidColorBrush x:Key="{local:ThemeResourcesThemeKey ResourceKey=Background, ThemeName=MetropolisDark}" Color="Black" /> <SolidColorBrush x:Key="{local:ThemeResourcesThemeKey ResourceKey=Foreground, ThemeName=MetropolisDark}" Color="Orange" />

Note:
Theme keys without the ThemeName property specified will be used when the default theme (DeepBlue) is applied.

Pros:
This approach has all the advantages of the previous one - no need to create styles/resources for each theme, theme specific colors can be used almost anywhere.
You have complete control over your controls' colors. There is no need to find DevExpress colors that will be appropriate for your scenario and depend on them.
Cons:
Just like the previous approach, custom theme keys won't allow you to create different templates for each theme (unless you create different templates with the same theme key and reference them in a single style using the ThemeResource extension).
It will be necessary to create resources (e.g., colors) with custom theme keys for every theme that you are going to use.

4. If you only need to change the Background and Foreground properties based on a currently selected theme, you can integrate the BackgroundPanel control into your control's template:

XAML
<ControlTemplate TargetType="{x:Type local:CustomControl4}"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <dx:BackgroundPanel> <TextBlock HorizontalAlignment="Center" Text="{TemplateBinding Text}" VerticalAlignment="Center"/> </dx:BackgroundPanel> </Border> </ControlTemplate>

BackgroundPanel's Background and Foreground properties are automatically changed for a current theme and inherited by all its descendants.

Note:
All DevExpress windows (DXWindow, DXDialog, DXRibbonWindow, etc.) already use BackgroundPanel in their content. If you use a DevExpress window, its content will use theme-specific background and foreground settings.

Pros:
You don't need to care about theme keys or theme-specific colors. BackgroundPanel will get proper colors automatically.
This approach can be used everywhere, not only in the Generic.xaml file, provided that you create a custom template for your control.
You can place BackgroundPanel into UserControls and Windows as well.
Cons:
The use case is pretty narrow. If you want to make something other than background or foreground colors theme-specific, or colors provided by BackgroundPanel are inappropriate in your scenario, you will have to use a different approach.
You will need to customize your control's template to put BackgroundPanel there.
If you use BackgroundPanel in your control, you will not be able to specify custom background and foreground values using corresponding properties at the control level.

5. The last approach allows you to bind to the name of the theme applied to a control. Then, you can use either a DataTrigger or a value converter to return required colors/resources:

XAML
<Style TargetType="local:CustomControl5"> <Style.Triggers> <DataTrigger Binding="{Binding Path=(dx:ThemeManager.TreeWalker).ThemeName, RelativeSource={RelativeSource Self}}" Value="MetropolisDark"> <Setter Property="BorderBrush" Value="DarkGray" /> </DataTrigger> </Style.Triggers> </Style>
XAML
<Setter Property="Foreground" Value="{Binding Path=(dx:ThemeManager.TreeWalker).ThemeName, RelativeSource={RelativeSource Self}, Converter={StaticResource ThemeNameToForegroundConverter}}" />
XAML
<dxmvvm:ObjectToObjectConverter x:Key="ThemeNameToForegroundConverter"> <dxmvvm:MapItem Source="DeepBlue" Target="DarkBlue" /> <dxmvvm:MapItem Source="MetropolisDark" Target="Orange" /> </dxmvvm:ObjectToObjectConverter>

Pros:
You can assign anything, including a custom template, to your control when a trigger or converter is used.
You won't need to create custom styles for every theme.
This approach can be used everywhere, not only in the Generic.xaml file.
Cons:
Creating triggers for every DevExpress theme will overcomplicate your style/template.
Styles with such bindings may be hard to read.

Conclusion:

There are plenty of ways to make your custom controls theme dependent. Some of them are pretty easy, while others, being more complex, give you better flexibility. Here, I tried to describe the most common scenarios. If you feel that none of the described approaches work for you, feel free to describe your task in comments.

Below you can find an example demonstrating all the described approaches in action:

Example:  E3524 - How to implement the ThemeMananger theme support in custom controls

See Also:
DevExpress WPF Themes
T361488 - How to find and change an inner DevExpress control template
T128436 - How to use DevExpress themes in a WPF Application
T207471 - How to use the ThemeResource extension to load resources from DevExpress themes dynamically

Show previous comments (1)
Alex Chuev (DevExpress) 10 years ago

    Thank you, Robert. I'm happy to hear that I could be of assistance.

    C C
    Customer3.14159 5 years ago

      Hello, can anyone tell me where to find

      XAML
      GridColumnHeaderThemeKey

      because I get the error "The name "GridColumnHeaderThemeKey" does not exist in the namespace "http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys".

      Thank you very much.

      Alexander Russkov (DevExpress) 5 years ago

        Hello,
        I've created a separate ticket on your behalf (T813575: GridColumnHeaderThemeKey cannot be found in the grid/themekeys namespace). It has been placed in our processing queue and will be answered shortly.

        Thanks,
        Alexander

        Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

        Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.