为什么我不能使用WPF 4.0中应用的Aero主题设置控件的样式?

dev*_*xer 13 .net wpf xaml .net-4.0 wpf-4.0

我最近将一个项目从WPF 3.5转换为WPF 4.0.功能上,一切正常,但我在Aero主题上应用的DataGrid风格突然停止工作.正如您从下面的前/后图片中看到的那样,我的DataGrids从Aero外观加上粗体标题,额外填充和交替行格式变成了简单的"Aero".除了删除所有对WPF工具包的引用(因为DataGrid现在是WPF 4.0的原生),我真的没有改变我的代码/标记.

之前(WPF Toolkit DataGrid)

看起来像Aero带有粗体标题,额外填充和备用行样式

之后(.NET 4.0 DataGrid)

看起来像Aero没什么

正如我在之前的一个问题中所了解到的,如果我停止引用Aero资源字典,我能够再次使用自定义DataGrid样式,但是在Windows XP上看起来一切都看起来都是"Luna"(这不是我想要的).

那么,我如何确保我的应用程序始终使用Aero主题,但仍然在WPF 4.0中的主题上应用样式?

这是我的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">
    <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>
Run Code Online (Sandbox Code Playgroud)

这是我的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">
        <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>
Run Code Online (Sandbox Code Playgroud)

这是一个用法示例:

<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>
Run Code Online (Sandbox Code Playgroud)

编辑

我刚想到可能问题是我引用了错误的Aero框架版本.

这就是我现在拥有的:

<ResourceDictionary
    Source="/PresentationFramework.Aero,
        Version=3.0.0.0,
        Culture=neutral,
        PublicKeyToken=31bf3856ad364e35,
        ProcessorArchitecture=MSIL;component/themes/aero.normalcolor.xaml" />
Run Code Online (Sandbox Code Playgroud)

它应该更新到4.0版吗?什么是PublicKeyToken版本4(或者我如何解决这个问题)?

Ale*_*erg 68

答案很长

之前的简短回答提供了一些XAML来解决问题,以及对导致问题的简要概述.

加载主题的资源与在操作系统级别更改主题不同.加载主题资源可能会产生不利影响.从WPF的角度来看,应用程序中现在存在大量隐式样式.这些样式可能胜过其他样式.底线是处理一个主题,如应用程序皮肤可能无法完成而无需改进.

以下详细答案将对该问题进行更深入的讨论.首先介绍一些背景主题.这将回答一些外围问题,它也将为理解手头的问题提供更好的基础.之后,将通过有效的调试策略解决和解决问题的各个方面.

主题与皮肤

这是一个很好的问题,部分原因是数百名博主和论坛主题建议从文件加载主题作为"改变主题"的方式.一些提出此建议的作者在微软工作,许多作者显然都是高素质的软件工程师.这种方法似乎在很多时候都有用.但是,正如您所注意到的,这种方法在您的方案中并不完全有效,并且需要进行一些改进.

其中一些问题源于不精确的术语.不幸的是,主题这个词已经无可救药地超载了.主题的精确定义可以避免混淆,这只是系统主题.一个系统主题定义计算机上Win32视觉效果的默认外观.我的操作系统是Vista.我安装的主题位于C:\ WINDOWS\Resources\Themes.在该文件夹中有两个文件:aero.theme和Windows Classic.theme.如果我想改变主题,我会去[Personalize | 主题]或[个性化| 窗口颜色和外观| 配色方案].虽然它并不是很明显,但我可以选择的选项可以归结为Aero或Classic以及其他一些改进.因为WPF窗口呈现其客户区而不是合成一堆Win32控件,所以客户区不会自动尊重主题.主题程序集(例如PresentationFramework.Aero.dll)为将主题功能扩展到WPF窗口提供了基础.

在此输入图像描述 在此输入图像描述

主题的更一般定义是任何外观和感觉配置,在任何粒度级别(OS,应用程序,控制).当人们使用一般定义时,可能存在各种程度的混淆.请注意,MSDN在没有警告的情况下在精确定义和一般定义之间切换!

很多人会说你正在加载应用程序皮肤,而不是主题.这两个词都可以说是正确的,但我会推荐这个心理模型,因为它会减少混淆.

那么,我如何确保我的应用始终使用Aero 主题 ...?[强调补充]

同样,可以说你正在加载Aero的资源作为皮肤.具体来说,您正在加载位于PresentationFramework.Aero.dll内的ResourceDictionary.这些资源以前是特殊待遇,因为它们是默认资源.但是,一旦进入应用程序,它们将被视为任何其他任意资源集合.当然,Aero的ResourceDictionary是全面的.由于它将在应用程序范围内加载,它将有效地隐藏主题(Luna,在您的情况下)提供的每个默认样式,以及导致问题的一些其他样式.请注意,最终,主题仍然是相同的(Luna).

如上所述,主题涉及样式优先级,它本身是依赖属性优先级的一种形式 .这些优先规则极大地揭示了问题中观察到的行为的神秘面纱.

明确的风格.Style属性直接设置.在大多数情况下,样式不是内联定义的,而是通过显式键引用为资源...

隐含的风格.Style属性未直接设置.但是,样式存在于资源查找序列(页面,应用程序)的某个级别,并使用与要应用样式的类型匹配的资源键进行键控...

默认样式,也称为主题样式.Style属性未直接设置,实际上将读为null ...在这种情况下,样式来自作为WPF表示引擎一部分的运行时主题评估.

博客条目更深入地了解样式与默认样式.

.NET装配检查

这也是一个很好的问题,部分原因是有很多活动部件. 没有有效的调试策略,几乎不可能理解正在发生的事情. 考虑到这一点,.NET装配检查是一个自然的起点.

From WPF’s point of view, a theme is essentially a ResourceDictionary serialized as BAML and embedded in a regular .NET assembly (e.g. PresentationFramework.Aero.dll). Later on, it will be necessary to view the themes as plain XAML to verify behavior in the problem.

Fortunately, Microsoft provides the 4.0 themes as XAML for developers’ convenience. I’m not sure if pre-4.0 themes are downloadable in any form from Microsoft.

For general assemblies (including pre-4.0 theme assemblies), you can use the (previously free) tool Reflector with the BamlViewer plugin to decompile the BAML back into XAML. Although not as flashy, ILSpy is a free alternative with a built in BAML decompiler.

在此输入图像描述

.NET程序集遍布整个硬盘驱动器,这有点令人困惑.这是他们在我的机器上的路径,我有一种直觉,有时候设法记住,没有反复试验.

Aero 3.0

C:\ Program Files\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.Aero.dll

Aero 4.0

C:\ Windows\Microsoft.NET \组件\ GAC_MSIL\PresentationFramework.Aero\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll

什么是版本4的PublicKeyToken(或者如何解决这个问题)?

最简单的方法是使用Reflector.PublicKeyToken与以前相同:31bf3856ad364e35

在此输入图像描述

此外,sn.exe(来自Windows SDK)可以提取程序集信息.

在我的机器上,命令是:

C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin>sn.exe -Tp "C:\WINDOWS\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework.Aero\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll"

在此输入图像描述

Loading Themes as Skins

Should (the PresentationFramework.Aero reference) be updated to version 4.0?

Most definitely. The DataGrid did not exist in the .NET FCL prior to 4.0. There are several ways to confirm this, but the most intuitive one is that, by your own admission, you previously accessed it via the WPF Toolkit. If you choose to not load PresentationFramework.Aero 4.0 in App.xaml, Aero’s DataGrid Style won’t be in the application resources.

Now, it turns out it doesn’t even matter. I’ll use the original XAML, break in the debugger when loading, and inspect the application-scoped resources.

在此输入图像描述

As expected, there are two ResourceDictionaries in the application’s MergedDictionaries property, and the first ResourceDictionary is allegedly the 3.0 version of PresentationFramework.Aero. However, I see there are 266 resources in the first ResourceDictionary. At this point, it just so happens I know there are 266 resources in the Aero 4.0 theme, and only 243 resources in the Aero 3.0 theme. Moreover, there’s even a DataGrid entry! This ResourceDictionary is, in fact, the Aero 4.0 ResourceDictionary.

Perhaps someone else can explain why WPF is loading the 4.0 assembly when 3.0 was explicitly specified. What I can tell you is if the projects are retargeted to .NET 3.0 (and the compile errors are fixed), the 3.0 version of Aero will be loaded instead.

在此输入图像描述 在此输入图像描述

As you correctly deduced, Aero 4.0 should be loaded anyway. It’s just useful to know what’s going on while debugging this.

?

Problem #1: Aero’s DataGrid Style is not Being Used

The DataGrid in this application will have zero or more Styles chained together depending on how you configure Style.BasedOn properties.

It will also have a default Style, which, in your case, is embedded in the Luna theme.

I knew just by looking at the original XAML there was a Style inheritance issue. The big DataGrid Style with ~20 Setters does not set its BasedOn property.

在此输入图像描述

You have a Style chain of length two and your default Style is from the Luna theme. The DataGrid Style in Aero’s ResourceDictionary is simply not being used.

There are two big questions here. First, how can something like this be debugged in the first place? Second, what are the implications?

?

Debugging Style Chains

I would recommend using Snoop and/or WPF Inspector to debug WPF issues like this.

Version 0.9.9 of WPF Inspector even has a Style chain viewer. (I must warn you that this feature is currently buggy and not very useful for debugging this part of the application. Also note that it chooses to depict the default Style as part of the chain.)

The power of these tools is their ability to view and edit values of deeply nested elements at run-time. You can simply mouse over an element and its information will immediately surface in the tool.

Alternatively, if you simply want to look at a top-level element like the DataGrid, name the element in the XAML (e.g. x:Name="dg"), then break in the debugger when loading and put the element’s name in a Watch window. There you can inspect the Style chain via the BasedOn property.

Below, I’ve broken in the debugger while using the solution XAML. The DataGrid has three Styles in the Style chain with 4, 17, and 9 Setters respectively. I can drill a little deeper and deduce the first Style is "DataGrid_FixedStyle". As expected, the second is the large, implicit DataGrid Style from the same file. Finally, the third Style appears to be from Aero’s ResourceDictionary. Note that the default Style is not represented in this chain.

在此输入图像描述

At this point it should be noted that there is actually no variation between each theme's DataGrid Style. You can verify this by taking the DataGrid Styles from their respective 4.0 themes, copying them into separate text files, and then comparing them with a diff tool.

In fact, a moderate number of Styles are simply identical from theme to theme. This is good to be aware of. To verify this, simply run a diff on the entire XAML held in two different themes.

在此输入图像描述

Note that there are many different elements nested within the DataGrid (e.g. DataGridRow) and each has its own Style. Even though DataGrid Styles are currently identical from theme to theme, the Styles for these nested elements could vary. Based on observed behavior in the problem, it's clear that some do. ?

Implications of Original XAML not Incorporating Aero’s DataGrid Style

Since DataGrid Styles are identical across the 4.0 themes, adding Aero’s DataGrid Style to the end of the Style chain is, in this case, basically superfluous. Aero’s DataGrid Style will be the same as the default DataGrid Style (from Luna, in your case). Of course, future themes could always have variations with respect to the DataGrid Style.

Regardless of whether there are any implications, since you intended to incorporate the Styles from Aero, it’s clearly more correct to do so until there’s a specific reason to not do so (which will be discussed later).

Most importantly, it's just useful to know what's going on.

Style.BasedOn Only Has Meaning within the Context in which it’s Used

In the solution XAML, DataGridResourceDictionary.xaml works exactly the way you want it to work. It’s important to understand why, and it’s important to understand that using it this way precludes using it in other ways.

Let’s say the final Styles in DataGridResourceDictionary.xaml’s Style chains set their BasedOn properties to a Type key (e.g. BasedOn="{StaticResource {x:Type DataGrid}}"). If they do that, then they will inherit from an implicit Style that matches this key. However, the Style that they inherit from depends on where DataGridResourceDictionary.xaml is loaded. If, for example, DataGridResourceDictionary.xaml is loaded into a merged dictionary right after Aero’s resources are loaded, then its Styles will inherit from the appropriate Aero Styles. Now, if, for example, DataGridResourceDictionary.xaml is the only ResourceDictionary loaded in the entire application, its Styles will actually inherit from the relevant Styles in the current theme (Luna, in your case). Note that the theme’s Styles will of course also be the default Styles!

在此输入图像描述

Now let’s say the final Styles in DataGridResourceDictionary.xaml’s Style chains do not set their BasedOn properties. If they do that, then they will be the final Styles in their respective Style chains, and the only other Styles evaluated will be the default Styles (always located in the theme). Note that this would kill your intended design of loading Aero as a skin and selectively refining parts of it.

Note that in the previous examples, if the final key was a string (e.g. x:Key="MyStringKey") instead of a Type, the same sorts of things would happen, but there would not be any matching Styles in the themes or in the Aero skin. An Exception would be thrown at load time. That said, dangling string keys could theoretically work if a context always existed in which a matching Style was found.

In the solution XAML, DataGridResourceDictionary.xaml has been modified. Styles at the end of each Style chain now inherit from an additional, implicit Style. When loaded in App.xaml, these will resolve to Aero Styles.

Problem #2: DataGrid.ColumnHeaderStyle and DataGrid.CellStyle

This is a nasty problem and it’s responsible for some of the strange behavior you saw. DataGrid.ColumnHeaderStyle and DataGrid.CellStyle get trumped by implicit DataGridColumnHeader and DataGridCell Styles. That is to say, they are incompatible with the Aero skin. Thus, they are simply removed from the solution XAML.

The rest of this subsection is a thorough investigation of the problem. DataGridColumnHeader and DataGridCell, like all FrameworkElements, have a Style property. In addition, there are a couple of very similar properties on DataGrid: ColumnHeaderStyle and CellStyle. You could call those two properties "helper properties". They map, at least conceptually, to DataGridColumnHeader.Style and DataGridCell.Style. How they actually get used though is undocumented, so we must dig deeper.

The properties DataGridColumnHeader.Style and DataGridCell.Style use value coercion. That means that when either Style is queried, special callbacks are used to determine what Style is actually returned to the caller (internal WPF code, for the most part). These callbacks can return any value they want. Ultimately, DataGrid.ColumnHeaderStyle and DataGrid.CellStyle are candidate return values in the respective callbacks.

With Reflector I can easily determine all of this. (If necessary, it’s also possible to step through .NET source code.) Starting in DataGridColumnHeader's static constructor, I locate the Style property and see it is being assigned additional metadata. Specifically, a coercion callback is specified. Beginning with that callback, I click through a sequence of method calls and quickly see what’s going on. (Note that DataGridCell does the same thing so I won't cover it.)

在此输入图像描述

The final method, DataGridHelper.GetCoercedTransferPropertyValue, essentially compares the source of DataGridColumnHeader.Style and DataGrid.ColumnHeaderStyle. Whichever source has higher precedence wins. The precedence rules in this method are based on Dependency Property predecence.

At this point, DataGrid.ColumnHeaderStyle will be inspected in both the original XAML and the solution XAML. A small matrix of information will be collected. Ultimately, this will explain the observed behavior in each application.

In the original XAML, I break in the debugger and see that DataGrid.ColumnHeaderStyle has a 'Style’ source. This makes sense as it was set inside a Style.

在此输入图像描述 在此输入图像描述

In the solution XAML, I break in the debugger and see that DataGrid.ColumnHeaderStyle has a 'Default’ source. This makes sense as that value was not set in a Style (or anywhere else).

在此输入图像描述 在此输入图像描述

The other value to inspect is DataGridColumnHeader.Style. DataGridColumnHeader is a deeply nested element that isn’t conveniently accessible when debugging in VisualStudio. Realistically, a tool like Snoop or WPF Inspector would be used to inspect the property.

With the original XAML, DataGridColumnHeader.Style has an 'ImplicitStyleReference’ source. This makes sense. DataGridColumnHeaders are instantiated deep down in internal WPF code. Their Style property is null and so they will look for an implicit Style. The tree is traversed from the DataGridColumnHeader element to the root element. As expected, no Styles are found. Then the application resources are checked. You have a string key ("DataGrid_ColumnHeaderStyle") set on the lone DataGridColumnHeader Style. This effectively hides it in this lookup and so it isn’t used. Then, the Aero skin is searched and a typical implicit Style is found. This is the Style that is used.

在此输入图像描述

If this step is repeated with the solution XAML, the result is the same: 'ImplicitStyleReference'. This time, however, the implicit Style is the lone DataGridColumnHeader Style in DataGridResourceDictionary.xaml, now implicitly keyed.

在此输入图像描述

Finally, if this step is repeated once again with the original XAML, and the Aero skin is not loaded, the result is now 'Default’! This is because there are simply no implicit DataGridColumnHeader Styles in the entire application.

Therefore, DataGrid.ColumnHeaderStyle will be used if the Aero skin is not loaded, but will not be used if the Aero skin is loaded! As advertised, loading a theme's resources may cause adverse effects.

It’s a lot to keep straight and the names all sound the same. The following diagram recaps all the action. Remember, the property with the higher precedence wins.

在此输入图像描述

It may not be what you want, but this is how the DataGrid works as of WPF 4.0. Taking this into account, you could theoretically set DataGrid.ColumnHeaderStyle and DataGrid.CellStyle at a very broad scope, and still have the ability to override the DataGridColumnHeader and DataGridCell Styles at a more narrow scope using implicit Styles.

Again, DataGrid.ColumnHeaderStyle and DataGrid.CellStyle get trumped by implicit DataGridColumnHeader and DataGridCell Styles. That is to say, they are incompatible with the Aero skin. Thus, they are simply removed from the solution XAML.

Problem #3: DataGridRow.Background

If the recommended changes up to this point have been implemented, something resembling the following should be on your screen. (Keep in mind I set my theme to Classic in order to debug this problem.)


Ale*_*erg 34

答案相对较短

加载主题的资源与在操作系统级别更改主题不同.加载主题资源可能会产生不利影响.从WPF的角度来看,应用程序中现在存在大量隐式样式.这些样式可能胜过其他样式.底线是处理一个主题,如应用程序皮肤可能无法完成而无需改进.

有一些模拟主题变化的替代方法.

这个问题展示了一些相当复杂的WPF功能,其中一部分似乎没有文档记录.但是,它似乎不是一个错误.如果它不是一个错误 - 也就是说,如果所有这些都是故意的WPF行为 - 你可能会认为WPF DataGrid在一些领域的设计很差.

Meleak的回答非常正确.但是,问题是可以解决的,可以在不影响您的设计或需要重复样式设置的情况下解决.也许更重要的是,问题是可调试的.

以下XAML有效.我留下旧的XAML注释掉只是为了使更改更加明显.要更深入地了解问题,请查看详细答案.

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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)