Почему я не могу настроить элемент управления с темой Aero, применяемой в WPF 4.0?

Недавно я преобразовал проект из WPF 3.5 в WPF 4.0. Функционально все работает, но стиль DataGrid, который я применял поверх темы Aero, внезапно прекратил работать. Как вы можете видеть из рисунков до/после ниже, мои DataGrids перешли от того, что Aero выглядят плюс смелые заголовки, дополнительные дополнения и чередующиеся форматы строк только для простого "Aero". Помимо удаления всех ссылок на WPF Toolkit (поскольку DataGrid теперь является родным для WPF 4.0), я действительно ничего не менял о своем коде/разметке.

До (WPF Toolkit DataGrid)

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

После (.NET 4.0 DataGrid)

Looks like Aero w/ nothing

Как я узнал в более раннем вопросе, я могу заставить пользовательский стиль DataGrid снова работать, если перестать ссылаться на словарь ресурсов Aero, но потом все выглядит "Луна" в Windows XP (это не то, что я хочу).

Итак, как я могу гарантировать, что мое приложение всегда использует тему 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>

Edit

Мне просто пришло в голову, что, возможно, проблема в том, что я ссылаюсь на неправильную версию инфраструктуры 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 (или как это понять)?

Ответы

Ответ 1

Относительно короткий ответ

Загрузка ресурсов тем не совпадает с изменением темы на уровне ОС. Загрузка ресурсов тем может привести к неблагоприятным последствиям. С точки зрения WPF в приложении теперь присутствует большое количество неявных стилей. Эти стили могут превзойти другие стили. В нижней строке рассматривается тема, такая как скин приложения, может работать без уточнений.

Есть несколько альтернативных способов моделирования изменения темы.

Эта проблема демонстрирует довольно сложную функциональность WPF, и часть ее, как представляется, недокументирована. Однако, похоже, это не ошибка. Если это не ошибка, то есть, если все это преднамеренное поведение WPF, вы вполне можете утверждать, что WPF DataGrid плохо разработан в нескольких областях.

Ответ Meleaks был на правильном пути. Однако проблема разрешима, и ее можно решить без ущерба для вашего дизайна или для повторной настройки стиля. И, что еще важнее, проблема отлаживается.

Работает следующий 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 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 
                    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>

Ответ 2

Длинный ответ

Предыдущий короткий ответ предоставляет некоторые XAML для устранения проблемы, а также краткое изложение того, что вызывает ее.

Загрузка ресурсов тем не совпадает с изменением темы на OS. Загрузка ресурсов тем может оказывать неблагоприятное воздействие. Из WPF точки зрения, большое количество скрытые стили теперь присутствуют в выражение. Эти стили могут превзойти другие стили. Нижняя строка рассматривая тему как приложение кожа может работать без уточнения.

Следующий длинный ответ даст более глубокое обсуждение проблемы. Сначала будут рассмотрены несколько основных тем. Это ответит на некоторые из заданных периферийных вопросов, и это также обеспечит лучшую основу для понимания проблем. После этого отдельные аспекты проблемы будут расчленены и рассмотрены с помощью эффективной стратегии отладки.

Тема против кожи

Это был большой вопрос отчасти потому, что сотни блоггеров и темы форума рекомендуют загружать тему из файла как способ "сменить тему". Некоторые из авторов, которые делают эту рекомендацию, работают в Microsoft, и многие из авторов, очевидно, являются высокопроизводительными инженерами программного обеспечения. Этот подход, похоже, работает большую часть времени. Однако, как вы заметили, этот подход не работал точно в вашем сценарии и требовал нескольких уточнений.

Некоторые из этих проблем проистекают из неточной терминологии. К сожалению, тема слов стала безнадежно перегружена. Точное определение темы, которое позволит избежать путаницы, - это просто системная тема. A системная тема определяет внешний вид визуальных образов Win32 на машине. Моя ОС - Vista. Мои установленные темы расположены в папке C:\WINDOWS\Resources\Themes. В этой папке находятся два файла: aero.theme и Windows Classic.theme. Если я хочу изменить тему, я перехожу к [Персонализировать | Тема] или [Персонализация | Цвет окна и внешний вид | Цветовая схема]. Хотя это не сразу очевидно, варианты, которые я могу выбрать из всех, сводятся к Aero или Classic плюс некоторые дополнительные уточнения. Поскольку окно WPF создает свою клиентскую область, а не компонует кучу элементов управления Win32, клиентская область не будет автоматически уважать тему. Сборки тем (например, PresentationFramework.Aero.dll) обеспечивают основу для расширения функциональности темы в окнах WPF.

enter image description hereenter image description here

Более общее определение темы - любая конфигурация любого вида и восприятия на любом уровне детализации (ОС, приложение, управление). Когда люди используют общее определение, существует вероятность различной степени замешательства. Обратите внимание, что MSDN переключается между точным определением и общим определением без предупреждения!

Многие люди скажут, что вы загружаете скин приложения, а не тему. Любое слово, возможно, правильно, но я бы рекомендовал эту ментальную модель просто потому, что она вызывает меньше путаницы.

Итак, как я могу гарантировать, что мое приложение всегда использует тему Aero ...? [Выделено добавлено]

Опять же, можно сказать, что вы загружаете ресурсы Eros в качестве скина. В частности, вы загружаете ResourceDictionary, расположенный внутри PresentationFramework.Aero.dll. Эти ресурсы ранее получали специальное лечение, поскольку они были дефолтными. Однако, как только внутри приложения, они будут рассматриваться как любой другой произвольный набор ресурсов. Конечно, Eros ResourceDictionary является исчерпывающим. Поскольку он будет загружен в области приложения, он эффективно скроет каждый стиль по умолчанию, предоставляемый темой (Luna, в вашем случае), плюс несколько других стилей, которые вызывают проблему. Обратите внимание, что в конечном итоге тема остается прежней (Luna).

Как упоминалось выше, тема связана с приоритетом стиля, которая сама по себе является формой Свойство приоритетности свойств. Эти правила приоритета сильно демистифицируют наблюдаемое поведение в задаче.

Явный стиль. Свойство Style устанавливается непосредственно. В большинстве сценариев, стиль не определен встроенным, но вместо этого упоминается как ресурс, с помощью явного ключа...

Неявный стиль, Свойство Style не задано напрямую. Однако Стиль существует на некотором уровне в последовательность поиска ресурсов (страница, приложение) и используется с помощью ключ ресурса, который соответствует типу стиль должен применяться к...

Стиль по умолчанию, также известный как стиль темы. Свойство Style не задано напрямую, и на самом деле читать как null... В этом случае стиль приходит из темы времени исполнения оценка, которая является частью WPF презентации.

Эта запись в блоге гораздо глубже смотрит на стиль и стиль по умолчанию.

Проверка сборки .NET

Это был также большой вопрос отчасти потому, что так много движущихся частей. Без эффективной стратегии отладки практически невозможно понять, что происходит. Имея это в виду, проверка сборки .NET - это естественное место для начала.

С точки зрения WPF, тема по существу представляет собой ResourceDictionary, сериализованную как BAML и встроенную в обычную сборку .NET(например, PresentationFramework.Aero.dll). Позже, будет необходимо просматривать темы как простой XAML для проверки поведения в проблеме.

К счастью, Microsoft предоставляет 4.0 темы как XAML для удобства разработчиков. Я не уверен, что темы до 4.0 будут загружаться в любой форме из Microsoft.

Для общих сборок (включая сборку темы до 4.0) вы можете использовать (ранее свободный) инструмент Reflector с помощью плагин BamlViewer для декомпиляции BAML обратно в XAML. Несмотря на то, что это не так ярко, ILSpy является бесплатной альтернативой со встроенным декомпилятором BAML.

enter image description here

сборки .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

Что такое PublicKeyToken для версии 4 (или как это понять)?

Самый простой способ - использовать Reflector. PublicKeyToken такой же, как и раньше: 31bf3856ad364e35

enter image description here

Кроме того, sn.exe (из SDK Windows) может извлекать информацию об сборке.

На моей машине команда:

C:\Program Files\Microsoft SDK\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"

enter image description here

Загрузка тем в качестве скинов

Должен (PresentationFramework.Aero ссылка) обновляется до версии 4.0?

Наиболее определенно. DataGrid не существовал в .NET FCL до 4.0. Существует несколько способов подтвердить это, но наиболее интуитивным является то, что по собственному признанию вы ранее обращались к нему через WPF Toolkit. Если вы решите не загружать PresentationFramework.Aero 4.0 в App.xaml, Eros DataGrid Style не будет в ресурсах приложений.

Теперь, оказывается, это даже не имеет значения. Я использую оригинальный XAML, ломаю отладчик при загрузке и проверяю ресурсы с областью приложения.

enter image description here

Как и ожидалось, в свойстве MergedDictionaries есть два ResourceDictionaries, а первый ResourceDictionary - версия 3.0 PresentationFramework.Aero. Тем не менее, я вижу, что в первом ResourceDictionary имеется 266 ресурсов. На данный момент, это просто так, я знаю, что в теме Aero 4.0 есть 266 ресурсов, и только 243 ресурса в теме Aero 3.0. Более того, theres даже запись DataGrid! Этот ResourceDictionary является, по сути, Aero 4.0 ResourceDictionary.

Возможно, кто-то еще может объяснить, почему WPF загружает сборку 4.0, когда явно указано значение 3.0. Я могу вам сказать, что если проекты перенацелены на .NET 3.0 (и исправлены ошибки компиляции), вместо этого будет загружена версия 3.0 Aero.

enter image description hereenter image description here

Как вы правильно вывели, Aero 4.0 должен быть загружен в любом случае. Его просто полезно знать, что происходит при отладке этого.

Проблема №1: Стиль Eros DataGrid не используется

В DataGrid в этом приложении будет добавлено ноль или более стилей в зависимости от того, как вы настраиваете свойства Style.BasedOn.

Он также будет иметь стиль по умолчанию, который, в вашем случае, встроен в тему Luna.

Я знал, что, глядя на исходный XAML, была проблема наследования стиля. Большой тип DataGrid с ~ 20 Setters не устанавливает свойство BasedOn.

enter image description here

У вас есть цепочка стиля длиной два, а стиль по умолчанию - из темы Luna. Стиль DataGrid в Eros ResourceDictionary просто не используется.

Здесь есть два больших вопроса. Во-первых, как можно что-то подобное отлаживать в первую очередь? Во-вторых, каковы последствия?

Цепочки цепочек отладки

Я бы рекомендовал использовать Snoop и/или WPF Inspector для отладки таких вопросов WPF.

Версия 0.9.9 WPF Inspector даже имеет средство просмотра в стиле. (Я должен предупредить вас, что эта функция в настоящее время не работает и не очень полезна для отладки этой части приложения. Также обратите внимание, что она выбирает отображение стиля по умолчанию как части цепочки.)

Силой этих инструментов является их способность просматривать и редактировать значения глубоко вложенных элементов во время выполнения. Вы можете просто навести указатель мыши на элемент, и его информация немедленно появится в инструменте.

В качестве альтернативы, если вы просто хотите посмотреть на элемент верхнего уровня, такой как DataGrid, назовите элемент в XAML (например, x: Name= "dg" ), затем переломите отладчик при загрузке и поместите элементы имя в окне просмотра. Там вы можете проверить цепочку стилей через свойство BasedOn.

Ниже Ive сломался в отладчике при использовании решения XAML. DataGrid имеет три стиля в цепочке стилей с 4, 17 и 9 сеттерами соответственно. Я могу просверлить немного глубже и вывести первый стиль "DataGrid_FixedStyle". Как и ожидалось, второй - это большой implicit стиль DataGrid из того же файла. Наконец, третий стиль выглядит из Eros ResourceDictionary. Обратите внимание, что стиль по умолчанию не представлен в этой цепочке.

enter image description here

На этом этапе следует отметить, что между каждой темой DataGrid Style нет различий. Вы можете проверить это, взяв стили DataGrid из своих 4.0 тем, скопировав их в отдельные текстовые файлы, а затем сравнив их с инструментом diff,

На самом деле, умеренное количество стилей просто идентично от темы к теме. Это хорошо знать. Чтобы убедиться в этом, просто запустите diff для всего XAML, который хранится в двух разных темах.

enter image description here

Обратите внимание, что в DataGrid (например, DataGridRow) есть много разных элементов, каждый из которых имеет свой собственный стиль. Несмотря на то, что стили DataGrid в настоящее время идентичны от темы к теме, стили для этих вложенных элементов могут различаться. Основываясь на наблюдаемом поведении в проблеме, ясно, что некоторые делают.

Последствия исходного XAML, не включающего стиль Eros DataGrid

Так как стили DataGrid идентичны по темам 4.0, добавление стиля Eros DataGrid в конец цепочки стилей в этом случае в основном является излишним. Стиль Eros DataGrid будет таким же, как стандартный стиль DataGrid (от Luna, в вашем случае). Конечно, в будущих темах всегда могут быть изменения в отношении стиля DataGrid.

Независимо от того, есть ли какие-либо последствия, поскольку вы намеревались включить стили из Aero, это явно более правильно, чтобы сделать это до тех пор, пока у вас не будет особой причины не делать этого (что будет обсуждаться позже).

Самое главное, просто полезно знать, что происходит.

Style.BasedOn имеет значение только в контексте, в котором используется

В решении XAML DataGridResourceDictionary.xaml работает точно так, как вы хотите, чтобы он работал. Его важно понять, почему, и его важно понимать, что использование его таким образом исключает его использование другими способами.

Предположим, что финальные стили в цепочках цепочек данных DataGridResourceDictionary.xamls задают свойства BasedOn для ключа Type (например, BasedOn = "{StaticResource {x: Type DataGrid}}" ). Если они это сделают, они наследуют от неявного стиля, который соответствует этому ключу. Однако стиль, который они наследуют, зависит от того, где загружается DataGridResourceDictionary.xaml. Если, например, DataGridResourceDictionary.xaml загружается в объединенный словарь сразу после загрузки ресурсов Eros, то его стили наследуются от соответствующих стилей Aero. Теперь, если, например, DataGridResourceDictionary.xaml является единственным ResourceDictionary, загруженным во все приложение, его стили фактически наследуются от соответствующих стилей в текущей теме (Luna, в вашем случае). Обратите внимание, что темы Styles, конечно же, будут стилями по умолчанию!

<Т411 >

Теперь скажем, что окончательные стили в цепочках данных DataGridResourceDictionary.xamls не устанавливают свойства BasedOn. Если они это сделают, то они будут последними стилями в их соответствующих стильных цепочках, и только другие оцениваемые стили будут стандартными стилями (всегда расположены в теме). Обратите внимание, что это убьет ваш предполагаемый дизайн загрузки Aero в качестве скина и выборочно уточняет его части.

Обратите внимание, что в предыдущих примерах, если конечный ключ был строкой (например, x: Key = "MyStringKey" ) вместо Type, произошли бы такие же вещи, но не было бы подходящих стилей в темы или в Aero skin. Исключение будет выбрано во время загрузки. Тем не менее, болтающиеся строковые ключи теоретически могут работать, если всегда существовал контекст, в котором был найден соответствующий стиль.

В решении XAML DataGridResourceDictionary.xaml был изменен. Стили в конце каждой цепочки стилей теперь наследуются от дополнительного, неявного стиля. При загрузке в App.xaml они будут разрешены для стилей Aero.

Проблема №2: DataGrid.ColumnHeaderStyle и DataGrid.CellStyle

Это неприятная проблема и ее ответственность за некоторые странные поведение вы видели. DataGrid.ColumnHeaderStyle и DataGrid.CellStyle превзойдены неявными стилями DataGridColumnHeader и DataGridCell. То есть они несовместимы с кожей Aero. Таким образом, они просто удаляются из решения XAML.

Остальная часть этого подраздела - это тщательное исследование проблемы. DataGridColumnHeader и DataGridCell, как и все элементы FrameworkElements, имеют свойство Style. Кроме того, в DataGrid есть несколько очень похожих свойств: ColumnHeaderStyle и CellStyle. Вы можете назвать эти два свойства "вспомогательными свойствами". Они отображают, по крайней мере, концептуально, DataGridColumnHeader.Style и DataGridCell.Style. Как они фактически используются, хотя и не документированы, поэтому мы должны копать глубже.

Свойства DataGridColumnHeader.Style и DataGridCell.Style используют значение принуждения. Это означает, что когда запрашивается либо стиль, специальные обратные вызовы используются для определения того, какой стиль фактически возвращается вызывающему (внутренний код WPF, по большей части). Эти обратные вызовы могут возвращать любое значение, которое они хотят. В конечном итоге DataGrid.ColumnHeaderStyle и DataGrid.CellStyle являются возвращаемыми значениями кандидатов в соответствующих обратных вызовах.

С рефлектором я могу легко определить все это. (При необходимости его также можно выполнить исходный код .NET.) Начиная с статического конструктора DataGridColumnHeader, я обнаруживаю свойство Style и вижу его назначаются дополнительные метаданные. В частности, указывается обратный вызов принуждения. Начиная с этого обратного вызова, я просматриваю последовательность вызовов методов и быстро вижу, что происходит. (Обратите внимание, что DataGridCell делает то же самое, поэтому я не буду его закрывать.)

enter image description here

Последний метод, DataGridHelper.GetCoercedTransferPropertyValue, по существу сравнивает источник DataGridColumnHeader.Style и DataGrid.ColumnHeaderStyle. Какой бы ни был источник с более высоким приоритетом. Правила приоритета в этом методе основаны на Зависимость свойства зависимостей.

В этот момент DataGrid.ColumnHeaderStyle будет проверяться как в исходном XAML, так и в решении XAML. Будет собрана небольшая матрица информации. В конечном итоге это объяснит наблюдаемое поведение в каждом приложении.

В исходном XAML я разбиваю отладчик и вижу, что DataGrid.ColumnHeaderStyle имеет "источник стиля". Это имеет смысл, поскольку оно было установлено внутри стиля.

enter image description hereenter image description here

В решении XAML я разбиваю отладчик и вижу, что DataGrid.ColumnHeaderStyle имеет "источник по умолчанию". Это имеет смысл, поскольку это значение не было установлено в стиле (или где-либо еще).

enter image description hereenter image description here

Другое значение для проверки - DataGridColumnHeader.Style. DataGridColumnHeader - это глубоко вложенный элемент, который не является удобным при отладке в VisualStudio. Реально, для проверки свойства использовался бы инструмент Snoop или WPF Inspector.

С исходным XAML DataGridColumnHeader.Style имеет источник ImplicitStyleReference. Это имеет смысл. DataGridColumnHeaders внедряются глубоко внутри внутреннего кода WPF. Свойство Style имеет значение null, и поэтому они будут искать неявный стиль. Дерево перемещается из элемента DataGridColumnHeader в корневой элемент. Как и ожидалось, стилей не найдено. Затем проверяются ресурсы приложения. У вас есть строковый ключ ( "DataGrid_ColumnHeaderStyle" ), установленный в одиночном стиле DataGridColumnHeader. Это эффективно скрывает его в этом поиске и поэтому не используется. Затем выполняется поиск Aero-оболочки и найден типичный неявный стиль. Это стиль, который используется.

enter image description here

Если этот шаг повторяется с решением XAML, результат будет таким же: "ImplicitStyleReference". На этот раз, однако, неявный стиль - это одиночный стиль DataGridColumnHeader в DataGridResourceDictionary.xaml, теперь неявно введенный ключ.

enter image description here

Наконец, если этот шаг снова повторяется с исходным XAML, а скин Aero не загружен, результат будет теперь "По умолчанию! Это связано с тем, что во всем приложении просто отсутствуют неявные стили DataGridColumnHeader.

Следовательно, DataGrid.ColumnHeaderStyle будет использоваться, если скин Aero не загружен, но не будет использоваться, если загрузится скин Aero! Как заявлено, загрузка тематических ресурсов может привести к неблагоприятным последствиям.

Его много, чтобы держать прямо, и все имена звучат одинаково. Следующая диаграмма отражает все действия. Помните, что выигрывает свойство с более высоким приоритетом.

enter image description here

Возможно, это не так, как вы хотите, но так работает DataGrid с WPF 4.0. Принимая это во внимание, теоретически вы можете установить DataGrid.ColumnHeaderStyle и DataGrid.CellStyle в очень широкой области и по-прежнему иметь возможность переопределять стили DataGridColumnHeader и DataGridCell в более узкой области с использованием неявных стилей.

Опять же, DataGrid.ColumnHeaderStyle и DataGrid.CellStyle получаются из-за неявных стилей DataGridColumnHeader и DataGridCell. То есть они несовместимы с кожей Aero. Таким образом, они просто удаляются из решения XAML.

Проблема №3: ​​DataGridRow.Background

Если рекомендуемые изменения до этого момента были реализованы, на вашем экране должно появиться что-то похожее на следующее. (Имейте в виду, что я поставил свою тему на "Классик", чтобы отладить эту проблему.)

enter image description here

DataGrid имеет вид Aero, но AlternatingRowBackground не соблюдается. Каждая другая строка должна иметь серый фон.

enter image description here

Используя методы отладки, обсуждаемые до сих пор, будет установлено, что это то же самое, что и проблема №2. В настоящее время загружается неявный стиль DataGridRow внутри скина Aero. DataGridRow.Background использует принудительное принуждение. DataGrid.AlternatingRowBackground - это значение кандидата, которое может быть возвращено в обратном вызове принуждения. DataGridRow.Background - еще один кандидат. Если эти значения берутся из этого значения, это повлияет на значение, которое выбирает обратный вызов принуждения.

В настоящее время это должно быть ясно, но если нет, его нужно повторить. Загрузка тематических ресурсов может привести к неблагоприятным последствиям.

Короткий ответ на эту подзадачу - DataGridRow.Background должен быть установлен только в теме. В частности, он не должен устанавливаться установщиком стиля в любом месте приложения. К сожалению, теперь именно то, что происходит на коже Aero. Существует как минимум два способа решения этой проблемы.

После Aero-оболочки может быть добавлен пустой неявный стиль. Это скрывает оскорбительный стиль в Aero. В пустом стиле нет значений, поэтому используются значения из стиля по умолчанию. В конце концов, это работает только потому, что стили DataGridRow одинаковы в каждой теме 4.0.

В качестве альтернативы Eros DataGridRow Style можно скопировать, можно установить фоновый сеттер, а оставшуюся часть стиля можно добавить после скина Aero. В решении XAML используется эта методика. Расширяя стиль, приложение, скорее всего, продолжит поиск Aero в будущих сценариях. Разделив это расширение в App.xaml, DataGridResourceDictionary.xaml можно использовать более свободно в других контекстах. Однако имейте в виду, что имеет смысл добавить его в DataGridResourceDictionary.xaml, в зависимости от того, как этот файл будет использоваться в будущем. С точки зрения этого приложения, в любом случае работает.

Проблема № 4: Макет DataGridColumnHeader

Окончательное изменение довольно поверхностное. Если приложение запускается после внесения рекомендуемых изменений до сих пор, DataGridColumnHeaders будут иметь контент, выравниваемый по левому краю, а не по центру. Эта проблема может быть легко просверлена с помощью Snoop или WPF Inspector. Корень проблемы, по-видимому, является DataGridColumnHeaders, HorizontalContentAlignment установлен в положение "Влево".

enter image description here

Установите для параметра "Растянуть" и он работает как ожидалось.

Взаимодействие между Свойства макета и TextBlock форматирование. Snoop и WPF Inspector позволяют экспериментировать и позволяют легко определить, что работает в любой конкретной ситуации.

Заключительные мысли

Подводя итог, загрузка ресурсов тем не совпадает с изменением темы на уровне ОС. Загрузка ресурсов тем может привести к неблагоприятным последствиям. С точки зрения WPF в приложении теперь присутствует большое количество неявных стилей. Эти стили могут превзойти другие стили. В нижней строке рассматривается тема, такая как скин приложения, может работать без уточнений.

Тем не менее, я не полностью продал текущую реализацию WPF в отношении "вспомогательных свойств" (например, DataGrid.ColumnHeaderStyle), используемого посредством обратного вызова принуждения с правилами приоритета. Мне нужно задаться вопросом, почему они не могут быть локально назначены по назначенным целям (например, DataGridColumnHeader.Style) во время инициализации, если цели еще не имеют явно назначенного значения. Я недостаточно думал об этом, чтобы знать, каковы могут быть различные проблемы, но если это возможно, это может сделать модель "вспомогательного свойства" более интуитивной, более совместимой с другими свойствами и более надежной.

Наконец, хотя это не было в центре внимания этого ответа, очень важно отметить, что загрузка ресурсов тем для имитации изменения темы особенно плоха, потому что есть значительная стоимость ремонтопригодности. Существующие стили в приложении не будут автоматически основываться на стилях внутри тем ResourceDictionary. Каждому стилю в приложении пришлось бы установить свойство BasedOn в ключе типа (или быть основанным, прямо или косвенно, на другом стиле, который делает). Это чрезвычайно обременительно и подвержено ошибкам. Кроме того, существуют затраты на поддержку в отношении настраиваемых пользовательских элементов. тематические ресурсы для этих настраиваемых элементов управления также должны быть загружены для осуществления этой симуляции. И, конечно же, после этого вы можете столкнуться с проблемами приоритета стиля, аналогичными тем, с которыми вы столкнулись здесь!

Во всяком случае, это более чем один способ обрезать WPF-приложение (каламбур не предназначен!). Я надеюсь, что этот ответ даст дополнительную информацию о вашей проблеме и поможет вам и другим людям решить подобные проблемы.

Ответ 3

Я думаю, что проблема не в 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, но это может быть неудобно, особенно если вы используете этот стиль во многих местах.

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