Почему я не могу стилизовать элемент управления с темой Aero, примененной в WPF 4.0? - PullRequest
13 голосов
/ 21 ноября 2010

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

До (WPG 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 все выглядит "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 ]

68 голосов
/ 09 апреля 2011

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

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

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

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

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

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

Часть этой проблемы проистекает из неточной терминологии. К сожалению, тема слова стала безнадежно перегруженной. Точное определение темы, которая поможет избежать путаницы, - это просто системная тема . системная тема определяет внешний вид Win32 по умолчанию на компьютере. Моя ОС - это 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 …? [Выделено добавлено]

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

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

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

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

Стиль по умолчанию , также известный как стиль темы . Свойство Style не устанавливается напрямую и фактически будет читать как ноль ... В этом случае стиль исходит из темы времени выполнения оценка, которая является частью 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 \ Справочные сборки \ 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 (из 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 "

enter image description here

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

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

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

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

enter image description here

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

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

enter image description here enter image description here

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

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

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

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

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

enter image description here

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

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

Отладка цепочек стилей

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

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

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

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

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

enter image description here

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

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

enter image description here

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

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

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

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

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

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

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

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

enter image description here

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

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

В решении 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 имеет источник 'Style'.Это имеет смысл, как это было установлено внутри стиля.

enter image description here enter image description here

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

enter image description here enter 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.

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

Проблема № 4: DataGridColumnHeader Layout

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

enter image description here

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

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

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

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

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

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

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

34 голосов
/ 29 марта 2011

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

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

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

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

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

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

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

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: /3006015/pochemu-ya-ne-mogu-stilizovat-element-upravleniya-s-temoi-aero-primenennoi-v-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>
0 голосов
/ 22 ноября 2010

Я думаю, что проблема не в 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}"
          ... >
...