.netwpfxaml.net-4.0wpf-4.0

Why can't I style a control with the Aero theme applied in WPF 4.0?


I recently converted a project from WPF 3.5 to WPF 4.0. Functionally, everything works, but the DataGrid style I was applying on top of the Aero theme has suddenly stopped working. As you can see from the before/after pictures below, my DataGrids went from having an Aero look plus bold headings, extra padding, and alternating row formats to just looking plain "Aero". Besides removing all references to the WPF Toolkit (since the DataGrid is now native to WPF 4.0), I really didn't change anything about my code/markup.

Before (WPF Toolkit DataGrid)

Looks like Aero w/ bold headings, extra padding, and alternate row styles

After (.NET 4.0 DataGrid)

Looks like Aero w/ nothing

As I learned in an earlier question, I am able to get the custom DataGrid styling to work again if I stop referencing the Aero resource dictionary, but then everything looks "Luna" on Windows XP (which is not what I want).

So, how do I ensure that my app always uses the Aero theme, but still apply styling on top of that theme in WPF 4.0?

Here is my App.xaml code:

<Application
    x:Class="TempProj.App" 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="/PresentationFramework.Aero,
                        Version=3.0.0.0,
                        Culture=neutral,
                        PublicKeyToken=31bf3856ad364e35,
                        ProcessorArchitecture=MSIL;component/themes/aero.normalcolor.xaml" />
                <ResourceDictionary Source="/CommonLibraryWpf;component/ResourceDictionaries/DataGridResourceDictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Here is my DataGridResourceDictionary.xaml code:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style x:Key="DataGrid_ColumnHeaderStyle" TargetType="DataGridColumnHeader">
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="TextBlock.TextAlignment" Value="Center" />
        <Setter Property="TextBlock.TextWrapping" Value="WrapWithOverflow" />
    </Style>
    <Style x:Key="DataGrid_CellStyle" TargetType="DataGridCell">
        <Setter Property="Padding" Value="5,5,5,5" />
        <Setter Property="TextBlock.TextAlignment" Value="Center" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="DataGridCell">
                    <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
                        <ContentPresenter />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style TargetType="DataGrid">
        <Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGrid_ColumnHeaderStyle}" />
        <Setter Property="CellStyle" Value="{StaticResource DataGrid_CellStyle}" />
        <Setter Property="Background" Value="White" />
        <Setter Property="AlternatingRowBackground" Value="#F0F0F0" />
        <Setter Property="VerticalGridLinesBrush" Value="LightGray" />
        <Setter Property="HeadersVisibility" Value="Column" />
        <Setter Property="SelectionMode" Value="Single" />
        <Setter Property="SelectionUnit" Value="FullRow" />
        <Setter Property="GridLinesVisibility" Value="Vertical" />
        <Setter Property="AutoGenerateColumns" Value="False" />
        <Setter Property="CanUserAddRows" Value="False" />
        <Setter Property="CanUserDeleteRows" Value="False" />
        <Setter Property="CanUserReorderColumns" Value="True" />
        <Setter Property="CanUserResizeColumns" Value="True" />
        <Setter Property="CanUserResizeRows" Value="False" />
        <Setter Property="CanUserSortColumns" Value="True" />
        <Setter Property="IsReadOnly" Value="True" />
        <Setter Property="BorderBrush" Value="#DDDDDD" />
        <Setter Property="HorizontalGridLinesBrush" Value="#DDDDDD" />
        <Setter Property="VerticalGridLinesBrush" Value="#DDDDDD" />
    </Style>
    <Style x:Key="DataGrid_FixedStyle" TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
        <Setter Property="CanUserReorderColumns" Value="False" />
        <Setter Property="CanUserResizeColumns" Value="False" />
        <Setter Property="CanUserResizeRows" Value="False" />
        <Setter Property="CanUserSortColumns" Value="False" />
    </Style>
</ResourceDictionary>

Here's a usage sample:

<DataGrid
    Grid.Row="0"
    Grid.Column="0"
    Style="{StaticResource DataGrid_FixedStyle}"
    ItemsSource="{Binding Coordinates}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding X}" Header="X" />
        <DataGridTextColumn Binding="{Binding Y}" Header="Y" />
        <DataGridTextColumn Binding="{Binding Z}" Header="Z" />
    </DataGrid.Columns>
</DataGrid>

Edit

It just occurred to me that maybe the problem is that I'm referencing the wrong version of the Aero framework.

Here's what I have now:

<ResourceDictionary
    Source="/PresentationFramework.Aero,
        Version=3.0.0.0,
        Culture=neutral,
        PublicKeyToken=31bf3856ad364e35,
        ProcessorArchitecture=MSIL;component/themes/aero.normalcolor.xaml" />

Should this be updated to version 4.0? What is the PublicKeyToken for version 4 (or how do I figure this out)?


Solution

  • Relatively Short Answer

    Loading a theme’s resources is not the same as changing the theme at the OS level. Loading a theme’s resources may cause adverse effects. From WPF’s point of view, a large number of implicit Styles are now present in the application. These Styles may trump other Styles. The bottom line is treating a theme like an application skin may not work without refinements.

    There are some alternative ways of simulating a theme change.

    This problem showcases some fairly complex WPF functionality, and a portion of it appears to be undocumented. However, it does not appear to be a bug. If it’s not a bug - that is, if all of it is intentional WPF behavior - you might well argue the WPF DataGrid is poorly designed in a few areas.

    Meleak’s answer was very much on the right track. However, the problem is solvable and it can be solved without compromising your design or requiring repetitive Style setting. And perhaps more importantly, the problem is debuggable.

    The following XAML works. I left old XAML commented out just to make the changes more visible. For a more in-depth look at the problem, please see the long answer.

    DataGridResourceDictionary.xaml:

    <ResourceDictionary    
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
        <!--
        <Style x:Key="DataGrid_ColumnHeaderStyle" TargetType="DataGridColumnHeader">
        -->
        <Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
    
            <!--New-->
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <!---->
    
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="TextBlock.TextAlignment" Value="Center" />
            <Setter Property="TextBlock.TextWrapping" Value="WrapWithOverflow" />
        </Style>
    
        <!--
        <Style x:Key="DataGrid_CellStyle" TargetType="DataGridCell">
        -->
        <Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
            <Setter Property="Padding" Value="5,5,5,5" />
            <Setter Property="TextBlock.TextAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <!--
                    <ControlTemplate TargetType="DataGridCell">
                        <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
                            <ContentPresenter />
                        </Border>
                    </ControlTemplate>
                    -->
                    <ControlTemplate TargetType="{x:Type DataGridCell}">
                        <Border 
                            Padding="{TemplateBinding Padding}"
                            Background="{TemplateBinding Background}" 
                            BorderBrush="{TemplateBinding BorderBrush}"  
                            BorderThickness="{TemplateBinding BorderThickness}" 
                            SnapsToDevicePixels="True">
                            <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
    
            <!--Additional Feature-->
            <!--
                Remove keyboard focus cues on cells and tabbing on cells when
                only rows are selectable and the DataGrid is readonly.
    
                Note that having some kind of keyboard focus cue is
                typically desirable.  For example, the lack of any keyboard 
                focus cues could be confusing if an application has multiple
                controls and each control is showing something selected, yet
                there is no keyboard focus cue.  It's not necessarily obvious
                what would happen if Control+C or Tab is pressed.
    
                So, when only rows are selectable and the DataGrid is readonly,
                is would be ideal to make cells not focusable at all, make
                the entire row focusable, and make sure the row has a focus cue.
                It would take much more investigation to implement this.
            -->
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=SelectionUnit}" Value="FullRow"/>
                        <Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=IsReadOnly}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <Setter Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Background}" />
                    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
                    <Setter Property="IsTabStop" Value="False" />
                </MultiDataTrigger>
            </Style.Triggers>
            <!---->
        </Style>
    
        <!--
        <Style TargetType="DataGrid">
        --> 
        <Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
    
            <!--Unworkable Design-->
            <!--
            <Setter Property="ColumnHeaderStyle" Value="{StaticResource DataGrid_ColumnHeaderStyle}" />
            <Setter Property="CellStyle" Value="{StaticResource DataGrid_CellStyle}" />
            -->
    
            <Setter Property="Background" Value="White" />
            <Setter Property="AlternatingRowBackground" Value="#F0F0F0" />
    
    
            <!--This was a duplicate of the final PropertySetter.-->
            <!-- 
            <Setter Property="VerticalGridLinesBrush" Value="LightGray" />
            -->
    
            <Setter Property="HeadersVisibility" Value="Column" />
            <Setter Property="SelectionMode" Value="Single" />
            <Setter Property="SelectionUnit" Value="FullRow" />
            <Setter Property="GridLinesVisibility" Value="Vertical" />
            <Setter Property="AutoGenerateColumns" Value="False" />
            <Setter Property="CanUserAddRows" Value="False" />
            <Setter Property="CanUserDeleteRows" Value="False" />
            <Setter Property="CanUserReorderColumns" Value="True" />
            <Setter Property="CanUserResizeColumns" Value="True" />
            <Setter Property="CanUserResizeRows" Value="False" />
            <Setter Property="CanUserSortColumns" Value="True" />
            <Setter Property="IsReadOnly" Value="True" />
            <Setter Property="BorderBrush" Value="#DDDDDD" />
            <Setter Property="HorizontalGridLinesBrush" Value="#DDDDDD" />
            <Setter Property="VerticalGridLinesBrush" Value="#DDDDDD" />
        </Style>
    
        <Style x:Key="DataGrid_FixedStyle" TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
            <Setter Property="CanUserReorderColumns" Value="False" />
            <Setter Property="CanUserResizeColumns" Value="False" />
            <Setter Property="CanUserResizeRows" Value="False" />
            <Setter Property="CanUserSortColumns" Value="False" />
        </Style>
    </ResourceDictionary>
    

    App.xaml:

    <Application    
        x:Class="TempProj.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        StartupUri="MainWindow.xaml">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <!--
                    <ResourceDictionary                    
                        Source="/PresentationFramework.Aero,                        
                                Version=3.0.0.0,                     
                                Culture=neutral,                        
                                PublicKeyToken=31bf3856ad364e35,                        
                                ProcessorArchitecture=MSIL;component/themes/aero.normalcolor.xaml" />
                    -->
                    <ResourceDictionary                    
                        Source="/PresentationFramework.Aero,                        
                                Version=4.0.0.0,                     
                                Culture=neutral,                        
                                PublicKeyToken=31bf3856ad364e35,                        
                                ProcessorArchitecture=MSIL;component/themes/aero.normalcolor.xaml" />
                    <!--New-->
                    <!--
                        This is a modified replica of the DataGridRow Style in the Aero skin that's 
                        evaluated next.  We are hiding that Style and replacing it with this.
                    -->
                    <ResourceDictionary>
                        <Style x:Key="{x:Type DataGridRow}" TargetType="{x:Type DataGridRow}">
                            <!--
                                DataGridRow.Background must not be set in this application.  DataGridRow.Background
                                must only be set in the theme.  If it is set in the application, 
                                DataGrid.AlternatingRowBackground will not function properly.
    
                                See: https://stackoverflow.com/questions/4239714/why-cant-i-style-a-control-with-the-aero-theme-applied-in-wpf-4-0
    
                                The removal of this Setter is the only modification we have made.
                            -->
                            <!--
                            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
                            -->
    
                            <Setter Property="SnapsToDevicePixels" Value="true"/>
                            <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
                            <Setter Property="ValidationErrorTemplate">
                                <Setter.Value>
                                    <ControlTemplate>
                                        <TextBlock Margin="2,0,0,0" VerticalAlignment="Center" Foreground="Red" Text="!" />
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="Template">
                                <Setter.Value>
                                    <ControlTemplate TargetType="{x:Type DataGridRow}">
                                        <Border x:Name="DGR_Border"
                                                Background="{TemplateBinding Background}"
                                                BorderBrush="{TemplateBinding BorderBrush}"
                                                BorderThickness="{TemplateBinding BorderThickness}"
                                                SnapsToDevicePixels="True">
                                            <SelectiveScrollingGrid>
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="Auto"/>
                                                    <ColumnDefinition Width="*"/>
                                                </Grid.ColumnDefinitions>
    
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="*"/>
                                                    <RowDefinition Height="Auto"/>
                                                </Grid.RowDefinitions>
    
                                                <DataGridCellsPresenter Grid.Column="1"
                                                 ItemsPanel="{TemplateBinding ItemsPanel}"
                                                 SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
    
                                                <DataGridDetailsPresenter  SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=AreRowDetailsFrozen, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}}"
                                                    Grid.Column="1" Grid.Row="1"
                                                    Visibility="{TemplateBinding DetailsVisibility}" />
    
                                                <DataGridRowHeader SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"  Grid.RowSpan="2"
                                                    Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Row}}"/>
                                            </SelectiveScrollingGrid>
                                        </Border>
                                    </ControlTemplate>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </ResourceDictionary>
                    <!---->
    
                    <ResourceDictionary Source="/CommonLibraryWpf;component/ResourceDictionaries/DataGridResourceDictionary.xaml" />
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>
    

    MainWindow.xaml:

    <Window 
        x:Class="TempProj.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <Vector3DCollection x:Key="Coordinates">
                <Vector3D X="1" Y="0" Z="0"/>
                <Vector3D X="0" Y="22" Z="0"/>
                <Vector3D X="0" Y="0" Z="333"/>
                <Vector3D X="0" Y="4444" Z="0"/>
                <Vector3D X="55555" Y="0" Z="0"/>
            </Vector3DCollection>
        </Window.Resources>
        <Grid>
            <DataGrid
                Grid.Row="0"    
                Grid.Column="0"    
                Style="{StaticResource DataGrid_FixedStyle}"
                ItemsSource="{StaticResource Coordinates}">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding X}" Header="X" />
                    <DataGridTextColumn Binding="{Binding Y}" Header="Y" />
                    <DataGridTextColumn Binding="{Binding Z}" Header="Z" />
                </DataGrid.Columns>
            </DataGrid>
        </Grid>
    </Window>