Common Style Issues when using the Theme property

Andrew Smith / Wednesday, December 09, 2009

Summary

There are a couple of Style related issues that I see often. The first is a case where you have set our Theme property but some of the elements do not appear to be following the look for that theme. The other is a case where you have created a Style for a given element but your Style is not being used. To determine what is happening it is helpful to understand how Styles are located/resolved by WPF. I’m going to assume that you have a basic understanding of defining and using Styles in WPF. If not, you may want to read through the Styling and Templating topic in Microsoft’s help first.

Background

In WPF, an element can be affected by up to 2 Style instances – the “local” style and the “theme” style. Note, the theme I am referring to here is the operating system theme (e.g. Aero, XP Blue, XP Silver, etc.) and not the Theme property that we expose on our controls. I’ll explain how our Theme property plays a role a little later.

The theme style is the Style that WPF uses as the default style for the element. Without going into too much detail as it isn’t relevant to these issues, essentially the WPF framework loads a Style for a given element based on the value of its DefaultStyleKey, which is usually the element’s Type, and the current OS theme name. Essentially this is the Style provided by the developer of the control.

The local style is the style provided by the application in which the element is being used. If the Style property of an element is set then that will be the local style. If the Style property is not set then WPF will attempt to locate an implicit style and use that as the local style. The implicit style is a resolution process that involves checking the Resources of the element and its ancestors for a Style where the key in the ResourceDictionary is the specific Type of the element. The first Style that is located is used as the local style. If one is not found then the Application’s Resources are checked, again using the specific Type of the element as the key used to locate the style in the dictionary. Note, when you define a Style in a ResourceDictionary in XAML, the TargetType is used as the key if you don’t explicitly specify the key.

If a local style is found, the setters and triggers of that style will take precedence over those of the theme style. It is also important to note that if the OverridesDefaultStyle property is set (directly on the element or in the local style) then WPF will not apply the information from the theme style on the element instance.

The Infragistics Theme property takes advantage of the implicit local style capability of WPF. When you set the Theme property on a control, the ThemeManager class loads a ResourceDictionary containing the resources defined for that Theme and places them into the MergedDictionaries of the Resources of that element.

Now that we understand how WPF locates Styles and how the Theme property is involved we can try to understand why the original issues were occuring.

Problem 1 - Element Not Using Theme

The first problem I mentioned was that while the Theme property was set, the element does not use the look for that Theme. There are actually a couple of scenarios under which this commonly occurs.

Scenario 1

The first situation is one where the element that you are using is a derived instance of our class – perhaps to isolate the contents of that element. For example, you might have derived from RibbonTabItem so that you could define the RibbonGroups and Tools used by that tab in isolation as opposed to defining them all within the Window that will contain the xamRibbon. E.g.

<igRibbon:RibbonTabItem x:Class="StyleIssues.DerivedNotUsingTheme.MyRibbonTabItem"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:igRibbon="http://infragistics.com/Ribbon"
   Header="Home">
    <igRibbon:RibbonGroup Caption="Clipboard">
...
    </igRibbon:RibbonGroup>
</igRibbon:RibbonTabItem>

And then use it within the ribbon by creating an instance of that derive tab.

<Window x:Class="StyleIssues.DerivedNotUsingTheme.TestWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="clr-namespace:StyleIssues.DerivedNotUsingTheme"
   xmlns:igRibbon="http://infragistics.com/Ribbon"
   Title="TestWindow" Height="300" Width="300">
    <StackPanel>
        <igRibbon:XamRibbon Theme="Office2k7Black">
            <local:MyRibbonTabItem />
           
            <igRibbon:RibbonTabItem Header="Other" />
        </igRibbon:XamRibbon>
    </StackPanel>
</Window>

What you will notice is that when you run the application, the tab will not appear correct when you hot track the mouse over the tab.

image

Recall that I mentioned that when WPF looks for the implicit local style that it will always use the actual Type of the class. Setting the Theme property puts a ResourceDictionary that contains amongst other things, a Style for the RibbonTabItem. However WPF is not looking for that Type; instead it is looking for a Style where the Key is MyRibbonTabItem in this case. There are a couple of ways to get around this problem. One would be to not create a derived class but instead just define the xaml without a backing class and use LoadComponent. I described this approach in a forum post here. Another approach would be to set the Style property of your instance to a DynamicResource where the key is the base type. E.g.

<igRibbon:RibbonTabItem x:Class="StyleIssues.DerivedNotUsingTheme.MyRibbonTabItem"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:igRibbon="http://infragistics.com/Ribbon"
    Style="{DynamicResource {x:Type igRibbon:RibbonTabItem}}"
   Header="Home">
    <igRibbon:RibbonGroup Caption="Clipboard">
...
    </igRibbon:RibbonGroup>
</igRibbon:RibbonTabItem>

Scenario 2

Another case where this occurs is if you explicitly or implicitly set the Style of an element. One of the most common examples for this is providing an EditorStyle for a Field. E.g.

<Window x:Class="StyleIssues.SetStyle.TestWindow"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:igDP="http://infragistics.com/DataPresenter"
   xmlns:igEditors="http://infragistics.com/Editors"
   xmlns:sys="clr-namespace:System;assembly=mscorlib"
   Title="TestWindow" Height="300" Width="600">
    <Window.Resources>
        <igEditors:ComboBoxItemsProvider x:Key="departments">
            <sys:String>Accounting</sys:String>
            <sys:String>Marketing</sys:String>
            <sys:String>Sales</sys:String>
            <sys:String>Admin</sys:String>
        </igEditors:ComboBoxItemsProvider>
       
        <Style x:Key="comboStyle" TargetType="igEditors:XamComboEditor">
            <Setter Property="ItemsProvider" Value="{StaticResource departments}" />
        </Style>
    </Window.Resources>
    <Grid>
        <igDP:XamDataGrid Theme="LunaOlive" BindToSampleData="True">
            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout>
                    <igDP:Field Name="department">
                        <igDP:Field.Settings>
                            <igDP:FieldSettings EditorStyle="{StaticResource comboStyle}" />
                        </igDP:Field.Settings>
                    </igDP:Field>
                </igDP:FieldLayout>
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>
    </Grid>
</Window>

image

Recall that when WPF is locating the local style, it will first check the Style Property. If that is set then that will be the local style and it will not attempt to look for an implicit style. Since the implicit style will not be used, the Style provided for the element from the Theme will not be used. One way to workaround this is to set the Based on of your Style to the Style for the Theme you are using. E.g.

   xmlns:igThemes="http://infragistics.com/Themes"
   Title="TestWindow" Height="300" Width="600">
    <Window.Resources>
...
       
        <Style x:Key="comboStyle" 
              TargetType="igEditors:XamComboEditor"
               BasedOn="{x:Static igThemes:EditorsLunaNormal.XamComboEditor}"
              >
            <Setter Property="ItemsProvider" Value="{StaticResource departments}" />
        </Style>
    </Window.Resources>

image

Note a similar issue would occur if instead of explicitly setting the Style property, you put a Style into the Resources of an element either in the target element itself or one of its ancestors between it and the element on which you set the Theme property. The workaround for this issue would be the same as the one described above – to set the BasedOn for the Style.

Problem 2 - Custom Style Not Used

The second problem I mentioned is a case where you have defined a Style for an element and that Style is not being picked up when the Theme property has been set. E.g.

    <Window.Resources>
        <Style TargetType="igWindows:TabItemEx">
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
    </Window.Resources>
    <Grid>
        <igWindows:XamTabControl Theme="Office2k7Black">
            <igWindows:TabItemEx Header="One" />
            <igWindows:TabItemEx Header="Two" />
            <igWindows:TabItemEx Header="Three" />
        </igWindows:XamTabControl>
    </Grid>

By now you can probably tell what the problem is. When you set the Theme property, Styles are put into the Resources of the control on which you set the Theme. WPF during its implicit Style search will encounter the Resource of that element (the xamTabControl in this case) first and will not continue and therefore would ignore any Style you might have placed into the the Window or Application Resources. The workaround is to move that Style into the Resources of the element on which you set the Theme. Note, since this Style will now be found first (since the ResourceDictionary provided by the Theme property is put into the Resources.MergedDictionaries and the MergedDictionaries are checked after the resources you place directly in the Resources) the Style provided by the Theme will not be used so you must also use the approach recommended in the second scenario for the first problem. E.g.

        <igWindows:XamTabControl Theme="Office2k7Black">
            <igWindows:XamTabControl.Resources>
                <Style TargetType="igWindows:TabItemEx"
                      BasedOn="{x:Static igThemes:PrimitivesOffice2k7Black.TabItemEx}">
                    <Setter Property="FontWeight" Value="Bold" />
                </Style>
            </igWindows:XamTabControl.Resources>
            <igWindows:TabItemEx Header="One" />
            <igWindows:TabItemEx Header="Two" />
            <igWindows:TabItemEx Header="Three" />
        </igWindows:XamTabControl>