代码之家  ›  专栏  ›  技术社区  ›  devuxer

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

  •  13
  • devuxer  · 技术社区  · 15 年前

    我最近将一个项目从WPF 3.5转换为WPF 4.0。从功能上讲,一切都正常,但我在Aero主题上应用的DataGrid样式突然停止工作。正如您从下面的前后图片中看到的,我的数据报从具有Aero外观加上粗体标题、额外的填充和交替的行格式变成了简单的“Aero”。除了删除对WPF工具包的所有引用(因为DataGrid现在是WPF 4.0的原生文件),我真的没有更改任何关于我的代码/标记的内容。

    之前(WPF工具包数据报)

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

    在(.NET 4.0 DataGrid)之后

    Looks like Aero w/ nothing

    我从中学到 an earlier question ,如果我停止引用Aero资源字典,我可以让自定义数据报样式再次工作,但在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>
    

    这是我的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>
    

    以下是使用示例:

    <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>
    

    编辑

    我突然想到,问题可能是我引用了错误的Aero框架版本。

    我现在有:

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

    是否应将此更新为4.0版?什么是 PublicKeyToken 对于版本4(或者我如何解决这个问题)?

    3 回复  |  直到 15 年前
        1
  •  34
  •   Community Mohan Dere    8 年前

    回答相对简短

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

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

    这个问题展示了一些相当复杂的WPF功能,其中的一部分似乎是未记录的。然而,它似乎不是一个bug。如果它不是一个bug,也就是说,如果它都是有意的WPF行为,那么您可能会认为WPF数据报在某些方面设计得很差。

    混战者的回答非常正确。但是,这个问题是可以解决的,并且可以在不影响设计或需要重复样式设置的情况下解决。也许更重要的是,问题是 可调试

    以下XAML有效。我把旧的xaml注释掉了,只是为了使更改更可见。要更深入地了解该问题,请参阅 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>
    

    主窗口.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>
    
        2
  •  68
  •   Community Mohan Dere    8 年前

    冗长的回答

    上一个 short answer 提供一些XAML来解决此问题,并简要总结导致此问题的原因。

    加载主题资源与在 操作系统级别。加载主题资源 可能导致不良反应。来自WPF_ 视角,大量 隐式样式现在出现在 应用程序。这些款式可能胜过 其他款式。底线是 将主题视为应用程序 没有皮肤就不能工作 改进。

    下面的长答案将对这个问题进行更深入的讨论。将首先介绍一些背景主题。这将回答一些外围问题,也将为理解手头的问题提供更好的基础。之后,将使用有效的调试策略对问题的各个方面进行分析和解决。

    主题与皮肤

    这是一个很好的问题,部分原因是数以百计的博客和论坛线程建议从文件中加载一个主题,作为一种改变主题的方法。一些提出这一建议的作者在微软工作,许多作者显然是高素质的软件工程师。这种方法 出现 经常工作。但是,正如您注意到的,这种方法在您的场景中并不完全有效,需要进行一些改进。

    其中一些问题源于不精确的术语。不幸的是,“主题”这个词已经严重超载。一个可以避免混淆的主题的精确定义是 系统主题 . 一 system theme 定义计算机上Win32 Visuals的默认外观。我的操作系统是Vista。我安装的主题位于c:\windows\resources\themes。该文件夹中有两个文件:aero.theme和windows classic.theme。如果要更改主题,请转到[个性化主题]或[个性化窗口颜色和外观颜色方案]。虽然不是很明显,但我可以从所有选项中选择,归结为Aero或Classic加上一些额外的改进。由于WPF窗口呈现其客户机区域而不是组合一组Win32控件,因此客户机区域不会自动尊重主题。主题程序集(例如presentationframework.aero.dll)为将主题功能扩展到WPF窗口提供了基础。

    enter image description here enter image description here

    主题的更一般的定义是任何外观和感觉配置,在任何粒度级别(操作系统、应用程序、控制)。当人们使用一般的定义时,有可能产生不同程度的混淆。注意,在没有警告的情况下,msdn在精确定义和一般定义之间切换!

    很多人会说你正在加载一个应用程序 皮肤 不是主题。这两个词都可以说是正确的,但我建议使用这种心理模式,因为它可以减少混淆。

    那么,我如何确保我的应用程序始终 使用Aero 主题 _?[强调 新增

    再说一次,可以说你是作为一个 皮肤 . 具体来说,您要重新加载presentationframework.aero.dll中的ResourceDictionary。这些资源以前被给予特殊处理,因为它们是默认资源。但是,一旦进入应用程序,它们将被视为任何其他任意资源集合。当然,Aero的资源字典是全面的。因为它将在应用程序范围内加载,所以它将有效地隐藏由主题提供的每个默认样式(在您的例子中是luna),以及一些其他样式,这导致了问题的产生。请注意,最终,主题仍然是相同的(Luna)。

    如上所述,主题涉及 Style precedence 它本身就是 Dependency Property precedence . 这些优先规则极大地解除了问题中观察到的行为的神秘性。

    显式样式 . 样式属性是直接设置的。在大多数情况下, 样式不是以内联方式定义的,但 而是作为资源引用, 按显式键

    隐式样式 . 未直接设置Style属性。但是, 样式存在于 资源查找顺序(第页,共页) 应用程序),并使用 与类型匹配的资源键 样式将应用于

    默认样式 ,也称为 主题样式 . Style属性不是直接设置的,实际上 读取为空…在这种情况下, 样式来自运行时主题 属于WPF的评估 演示引擎。

    这个 blog entry 对样式和默认样式进行更深入的研究。

    .NET程序集检查

    这也是一个很好的问题,部分原因是运动部件太多了。 如果没有有效的调试策略,几乎不可能理解发生了什么。 有鉴于此,.NET组装检查是一个自然的开始。

    从wpf_的角度来看,主题本质上是一个序列化为BAML并嵌入到常规.NET程序集中的资源字典(例如presentationframework.aero.dll)。稍后,有必要将主题视为纯XAML来验证问题中的行为。

    幸运的是,微软提供了 4.0 themes 作为开发人员方便的XAML。我不确定是否可以从Microsoft以任何形式下载4.0之前的主题。

    对于常规程序集(包括4.0之前的主题程序集),可以使用(以前是免费的)工具 Reflector BamlViewer plugin 将BAML反编译回XAML。虽然没有那么华丽, ILSpy 是内置BAML反编译程序的免费替代方案。

    enter image description here

    .NET程序集在您的硬盘驱动器中随处可见,并且 it’s kind of confusing . 这是他们在我的机器上的路径,我有一种直觉,有时不经尝试和错误就能记住。

    航空3.0

    C:\程序文件\引用程序集\microsoft\framework\v3.0\presentationframework.aero.dll

    航空4.0

    C:\windows\microsoft.net\assembly\gac_u msil\presentationframework.aero\v4.0_4.0.0.0_uu31bf3856ad364e35\presentationframework.aero.dll

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

    最简单的方法是使用反射镜。PublicKeyToken与以前相同:31BF3856AD364E35

    enter image description here

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

    在我的机器上,命令是:

    C:\Program Files\Microsoft SDK\Windows\V7.1\bin>sn.exe-tp“C:\Windows\Microsoft.NET\assembly\GAC\MSIL\presentationframework.aero\v4.0.0.0\uuu 31bf3856ad364e35\presentationframework.aero.dll”

    enter image description here

    将主题作为外观加载

    应该(presentationframework.aero 参考)是否更新到4.0版?

    非常肯定。4.0之前的.NET FCL中不存在DataGrid。有几种方法可以证实这一点,但最直观的方法是,通过您自己的承认,您以前通过WPF工具包访问过它。如果选择不在app.xaml中加载presentationframework.aero 4.0,则应用程序资源中将不包含Aero DataGrid样式。

    现在,事实证明这根本不重要。我将使用原始XAML,加载时中断调试器,并检查应用程序范围的资源。

    enter image description here

    正如所料,应用程序的MergedDictionaries属性中有两个ResourceDictionary,第一个ResourceDictionary据称是PresentationFramework.Aero的3.0版本。不过,我知道 二百六十六 第一个资源字典中的资源。在这一点上,我知道Aero4.0主题中有266个资源,而Aero3.0主题中只有243个资源。此外,还有一个数据报条目!实际上,这个ResourceDictionary是Aero 4.0 ResourceDictionary。

    也许其他人可以解释当显式指定了3.0时,WPF为什么要加载4.0程序集。我可以告诉你的是,如果项目被重定为.NET 3.0(编译错误是固定的),那么将改为加载3.0版本的Aero。

    enter image description here enter image description here

    正如您正确推断的,Aero 4.0无论如何都应该加载。在调试时知道发生了什么是很有用的。

    小精灵

    问题1:未使用Aeros数据报样式

    此应用程序中的DataGrid将有零个或多个样式链接在一起,具体取决于如何配置Style.Basedon属性。

    它还将有一个默认样式,在您的情况下,它嵌入到Luna主题中。

    我只通过查看原始的XAML就知道存在样式继承问题。具有~20个setter的大数据报样式没有设置其basedon属性。

    enter image description here

    您有一个长度为2的样式链,默认样式来自luna主题。Aero的ResourceDictionary中的DataGrid样式只是没有被使用。

    这里有两个大问题。首先,如何在第一时间调试类似的东西?第二,这意味着什么?

    小精灵

    调试样式链

    我建议使用 Snoop 和/或 WPF Inspector 调试这样的WPF问题。

    WPF Inspector的0.9.9版本甚至有一个样式链查看器。(我必须警告您,此功能当前有问题,对于调试应用程序的这一部分不太有用。还要注意,它选择将默认样式描述为链的一部分。)

    这些工具的功能是查看和编辑 深嵌套 元素位于 运行时间 . 您可以简单地将鼠标悬停在一个元素上,它的信息将立即显示在工具中。

    或者,如果您只想查看像DataGrid这样的顶级元素,请在XAML中命名该元素(例如x:name=“dg”),然后在加载时中断调试器,并将元素名称放在监视窗口中。在那里,您可以通过basedon属性检查样式链。

    下面,我在使用解决方案XAML时在调试器中进行了中断。数据报在样式链中有三种样式,分别有4个、17个和9个setter。我可以钻得更深一点,推断出第一种样式是DataGrid _FixedStyle。正如所料,第二个是大的, implicit 来自同一文件的数据报样式。最后,第三种风格似乎来自于Aero的ResourceDictionary。请注意,默认样式不在此链中表示。

    enter image description here

    此时应该注意的是,每个主题的DataGrid样式实际上没有变化。您可以通过从各自的 4.0主题 ,将它们复制到单独的文本文件中,然后与diff工具进行比较。

    事实上,中等数量的款式 完全相同的 从主题到主题。这是值得注意的。要验证这一点,只需对包含在两个不同主题中的整个XAML运行diff。

    enter image description here

    请注意,有许多不同的元素嵌套在DataGrid中(例如,DataGridRow),每个元素都有自己的样式。尽管DataGrid样式当前在主题和主题之间是相同的,但是这些嵌套元素的样式可能会有所不同。根据在问题中观察到的行为,很明显有些是这样的。 小精灵

    原始XAML不包含Aero_s DataGrid样式的含义

    由于在4.0主题中,数据报样式是相同的,在这种情况下,在样式链的末尾添加AerosDataGrid样式是, 基本上 多余的。Aero的DataGrid样式将与默认的DataGrid样式相同(在您的情况下,来自Luna)。当然,未来的主题在数据报样式方面总是有变化的。

    不管是否有任何影响,因为您打算采用Aero的样式,这样做显然更正确,直到有特定的原因不这样做(稍后将讨论)。

    最重要的是,知道发生了什么很有用。

    style.basedon仅在其使用的上下文中有意义

    在解决方案xaml中,dataGridResourceDictionary.xaml的工作方式与您希望的工作方式完全相同。理解原因是很重要的,理解以这种方式使用它会妨碍以其他方式使用它也是很重要的。

    让s说DataGridResourceDictionary中的最终样式。xaml s样式链将其basedon属性设置为类型键(例如basedon=“staticresource x:type datagrid”)。如果这样做,那么它们将继承与此键匹配的隐式样式。但是,它们继承自的样式取决于加载DataGridResourceDictionary.xaml的位置。例如,如果在加载Aero_窋的资源后,将dataGridResourceDictionary.xaml加载到合并字典中,则其样式将从相应的Aero样式继承。现在,例如,如果DataGridResourceDictionary.xaml是整个应用程序中加载的唯一一个ResourceDictionary,那么它的样式实际上将从当前主题中的相关样式继承(在您的例子中是Luna)。请注意,主题样式当然也是默认样式!

    enter image description here

    现在让我们说一下DataGridResourceDictionary.xamls样式链中的最终样式 设置它们的basedon属性。如果这样做,那么它们将成为各自样式链中的最终样式,并且只评估其他样式为默认样式(始终位于主题中)。请注意,这将扼杀您将Aero作为皮肤加载的预期设计,并有选择地对其部分进行细化。

    请注意,在前面的示例中,如果最后一个键是字符串(例如x:key=“myStringkey”)而不是类型,则会发生相同的情况,但主题或Aero外观中不会有任何匹配的样式。加载时将引发异常。也就是说,如果始终存在一个匹配样式的上下文,悬空字符串键理论上可以工作。

    在解决方案xaml中,DataGridResourceDictionary.xaml已被修改。每个样式链末尾的样式现在继承自一个附加的隐式样式。当加载到app.xaml中时,这些将解析为Aero样式。

    问题2:DataGrid.ColumnHeaderStyle和DataGrid.CellStyle

    这是一个棘手的问题,它导致了你所看到的一些奇怪的行为。DataGrid.ColumnHeaderStyle和DataGrid.CellStyle通过隐式DataGridColumnHeader和DataGridCell样式获得优势。也就是说,它们与航空皮肤不相容。因此,它们只是从解决方案XAML中移除。

    本小节的其余部分是对该问题的彻底调查。 与所有框架元素一样,DataGridColumnHeader和DataGridCell具有Style属性。此外,在DataGrid上还有两个非常相似的属性:ColumnHeaderStyle和CellStyle。您可以调用这两个属性助手属性。它们至少在概念上映射到DataGridColumnHeader.Style和DataGridCell.Style。然而,它们是如何被利用的还没有正式记录,所以我们必须深入挖掘。

    属性DataGridColumnHeader.Style和DataGridCell.Style使用 value coercion . 这意味着,当查询任何一种样式时,都会使用特殊的回调来确定实际返回给调用者的样式(大部分是内部WPF代码)。这些回调可以返回 任何 他们想要的价值。最后,datagrid.columnHeaderStyle和datagrid.cellstyle 候选人 在各自的回调中返回值。

    使用反射镜,我可以很容易地确定所有这些。(如有必要,也可以 step through .NET source code 从DataGridColumnHeader的静态构造函数开始,我找到Style属性,并看到它被分配了额外的元数据。具体来说,指定了强制回调。从回调开始,我单击一系列方法调用,然后快速查看发生了什么。(请注意,DataGridCell执行相同的操作,因此我不会覆盖它。)

    enter image description here

    最后一个方法,DataGridHelper.getCoercedTransferPropertyValue,实质上比较了DataGridColumnHeader.Style和DataGrid.ColumnHeaderStyle的源。哪个震源具有更高的优先级就获胜。此方法中的优先规则基于 Dependency Property predecence .

    此时,将在原始XAML和解决方案XAML中检查datagrid.columnHeaderStyle。将收集一个小的信息矩阵。最后,这将解释在每个应用程序中观察到的行为。

    在最初的XAML中,我闯入调试器,看到datagrid.columnHeaderStyle有一个样式源。这是有意义的,因为它是在一个样式中设置的。

    enter image description here enter image description here

    在解决方案xaml中,我打开调试器,看到datagrid.columnHeaderStyle有一个默认源。这是有意义的,因为该值不是在样式(或其他任何地方)中设置的。

    enter image description here enter image description here

    要检查的另一个值是DataGridColumnHeader.Style。DataGridColumnHeader是一个嵌套很深的元素,在VisualStudio中调试时不方便访问。实际上,像snoop或wpf-inspect or这样的工具将用于检查财产。

    对于原始XAML,DataGridColumnHeader.Style有一个隐式的StyleReference源。这是有道理的。DataGridColumnHeaders在内部WPF代码中被实例化。它们的Style属性为空,因此它们将查找隐式样式。树从dataGridColumnHeader元素遍历到根元素。如预期,未找到样式。然后检查应用程序资源。在单独的DataGridColumnHeader样式上设置了一个字符串键(“DataGrid_ColumnHeaderStyle”)。这有效地将其隐藏在这个查找中,因此它不被使用。然后,对航空蒙皮进行搜索,找到一种典型的隐式风格。这是使用的样式。

    enter image description here

    如果此步骤与解决方案XAML一起重复,则结果相同:“implicitStyleReference”。然而,这次隐式样式是DataGridResourceDictionary.xaml中唯一的DataGridColumnHeader样式,现在已隐式设置了键。

    enter image description here

    最后,如果使用原始XAML再次重复此步骤, 空气动力蒙皮没有加载 ,结果现在是__default_!这是因为整个应用程序中没有隐式DataGridColumnHeader样式。

    因此,如果未加载Aero外观,将使用DataGrid.ColumnHeaderStyle,但如果加载Aero外观,则不会使用DataGrid.ColumnHeaderStyle!如广告所示, 加载主题的资源可能会造成不利影响 .

    保持笔直是很重要的,而且名字听起来都一样。下图概述了所有操作。记住,具有更高优先级的属性将获胜。

    enter image description here

    它可能不是您想要的,但这是从WPF 4.0开始DataGrid的工作方式。考虑到这一点,理论上可以将datagrid.columnHeaderStyle和datagrid.cellstyle设置在非常广泛的范围内,并且仍然能够使用隐式样式在更窄的范围内覆盖datagridColumnHeader和datagridCell样式。

    同样,DataGrid.ColumnHeaderStyle和DataGrid.CellStyle通过隐式DataGridColumnHeader和DataGridCell样式获得优势。也就是说,它们与航空皮肤不相容。因此,它们只是从解决方案XAML中移除。

    问题3:DataGridRow.Background

    如果在这一点上已经实现了建议的更改,那么屏幕上应该会出现类似以下内容的内容。(请记住,为了调试这个问题,我将主题设置为Classic。)

    enter image description here

    数据报具有Aero外观,但AlternatingRowBackground不受尊重。每隔一行都应该有灰色背景。

    enter image description here

    使用目前讨论的调试技术,我们会发现 这与问题2完全相同。 . 正在加载Aero外观中的隐式DataGridRow样式。DataGridRow.Background使用属性强制。DataGrid.AlternatingRowBackground是 候选人 可能在强制回调中返回的值。DataGridRow.background是另一个 候选人 这些值的来源将影响强制回调选择的值

    现在应该很清楚了,但如果不清楚,就必须重申。 加载主题的资源可能会造成不利影响。

    此子问题的简短答案是DataGridRow。只能在主题中设置背景。具体来说,它不能由样式设置器在应用程序的任何地方设置。不幸的是,这正是空气动力学皮肤发生的事情。解决这个问题至少有两种方法。

    可以在Aero外观之后添加空白隐式样式。这隐藏了Aero中的冒犯风格。在空白样式中没有值,所以默认样式的值最终被使用。最后,这只起作用,因为每个4.0主题中的DataGridRow样式都是相同的。

    或者,可以复制Aero的DataGridRow样式,删除背景设置器,并在Aero外观之后添加样式的其余部分。解决方案XAML使用了这种技术。通过扩展样式,应用程序在未来的场景中更可能继续寻找Aero。通过在app.xaml中隔离此扩展,可以在其他上下文中更自由地使用DataGridResourceDictionary.xaml。但是,请注意,将其添加到DataGridResourceDictionary.xaml可能更有意义,这取决于将来如何使用该文件。对于这个应用程序,任何一种方法都有效。

    问题4:DataGridColumnHeader布局

    最后的改变是相当肤浅的。如果在进行建议的更改之后运行应用程序,则DataGridColumnHeaders的内容将左对齐,而不是居中。使用Snoop或WPF检查员可以很容易地钻取这个问题。问题的根源似乎是DataGridColumnHeaders HorizontalContentAlignment 设置为“左”。

    enter image description here

    将其设置为__Stretch_,它将按预期工作。

    两者之间有一些相互作用 Layout properties TextBlock 格式化属性。snoop和wpf检查器允许进行实验,并使其易于确定在任何给定情况下工作的内容。

    最后的想法

    总而言之,加载主题资源与在操作系统级别更改主题不同。加载主题资源可能会造成不利影响。从wpf_的角度来看,应用程序中现在存在大量隐式样式。这些款式可能胜过其他款式。底线是将一个主题像应用程序皮肤一样处理,如果没有改进,它可能无法工作。

    也就是说,对于通过具有优先规则的强制回调使用的“助手属性”(例如,datagrid.columnheaderstyle),我在当前WPF实现上没有完全出售。我想知道,如果目标还没有显式分配值,为什么它们可以在初始化时只在本地分配给它们的目标(例如dataGridColumnHeader.Style)。我还没有充分考虑到这一点,不知道各种各样的问题可能是什么,但是如果可能的话,它可能会使“助手属性”模型更直观,更符合其他属性,更简单。

    最后,尽管它不是这个答案的焦点,但需要注意的是,加载主题资源来模拟更改主题是非常糟糕的,因为 可观的可维护性成本 . 应用程序中的现有样式不会自动基于主题资源字典中的样式。应用程序中的每个样式都必须将其based on属性设置为类型键(或者直接或间接基于另一种样式)。这是非常繁重和容易出错的。此外,对于主题感知的自定义控件,还存在可维护性成本。这个 theme resources 对于这些自定义控件,还必须加载以实现此模拟。当然,这样做之后,您可能会面临与此处所面临的样式优先问题类似的样式优先问题!

    不管怎样,有不止一种方式可以让WPF应用程序(没有双关语!).我希望这个答案能对你的问题有更多的了解,并帮助你和其他人解决类似的问题。

        3
  •  0
  •   Fredrik Hedblad    15 年前

    我认为问题不在于PresentationFramework.Aero本身,而是在于通过包含它来获得隐式的DataGrid样式。这也可以通过在app.xaml中添加这个来看到。

    <Application.Resources>
        <Style TargetType="{x:Type DataGridColumnHeader}"/>
    </Application.Resources>
    

    如果没有显式设置,这将导致所有DataGridColumnHeader的样式丢失。

    这就行了

    <DataGrid ColumnHeaderStyle="{StaticResource DataGrid_ColumnHeaderStyle}" ../>
    

    但是,这不会

    <DataGrid Style="{StaticResource DataGrid_FixedStyle}" ../>
    
    <Style x:Key="DataGrid_FixedStyle" TargetType="DataGrid">
        <Setter Property="ColumnHeaderStyle"
                Value="{StaticResource DataGrid_ColumnHeaderStyle}" />
    </Style>
    

    我不确定有什么好办法解决这个问题。我唯一能想到的是在数据报本身上显式地设置所有样式,但这可能不方便,特别是在许多地方使用这种样式时。

    <DataGrid Style="{StaticResource DataGrid_FixedStyle}"
              ColumnHeaderStyle="{StaticResource DataGrid_ColumnHeaderStyle}"
              CellStyle="{StaticResource DataGrid_CellStyle}"
              ... >