Длинный ответ
Предыдущий краткий ответ содержит некоторые XAML для устранения проблемы, а также краткое изложение причин, по которым она возникла.
Загрузка ресурсов темы - это не то же самое, что изменение темы на
Уровень ОС. Загрузка ресурсов темы
может вызвать неблагоприятные последствия. Из WPF
точка зрения, большое количество
неявные стили теперь присутствуют в
приложение. Эти стили могут превзойти
другие стили Суть в том,
рассматривая тему как приложение
кожа может не работать без
уточнения.
Следующий длинный ответ предоставит более глубокое обсуждение проблемы. Сначала будут рассмотрены несколько фоновых тем. Это ответит на некоторые второстепенные вопросы и предоставит лучшую основу для понимания имеющихся проблем. После этого отдельные аспекты проблемы будут проанализированы и решены с помощью эффективной стратегии отладки.
Тема против кожи
Это был отличный вопрос отчасти потому, что сотни блогеров и веток форума рекомендуют загружать тему из файла, чтобы «изменить свою тему». Некоторые из авторов, делающих эту рекомендацию, работают в Microsoft, и многие из авторов, очевидно, являются разработчиками программного обеспечения высокого уровня. Этот подход , по-видимому, работает большую часть времени. Однако, как вы заметили, этот подход не совсем работал в вашем сценарии и требовал ряда улучшений.
Часть этой проблемы проистекает из неточной терминологии. К сожалению, тема слова стала безнадежно перегруженной. Точное определение темы, которая поможет избежать путаницы, - это просто системная тема . системная тема определяет внешний вид Win32 по умолчанию на компьютере. Моя ОС - это Vista. Мои установленные темы находятся в C: \ WINDOWS \ Resources \ Themes. В этой папке находятся два файла: aero.theme и Windows Classic.theme. Если я хочу изменить тему, я перехожу к [Персонализировать | Тема] или [Персонализация | Цвет окна и внешний вид | Цветовая схема]. Хотя это не сразу очевидно, варианты, которые я могу выбрать, сводятся к Aero или Classic, а также к некоторым дополнительным улучшениям. Поскольку окно WPF визуализирует свою клиентскую область, а не создает группу элементов управления Win32, клиентская область не будет автоматически соответствовать теме. Сборки тем (например, PresentationFramework.Aero.dll) служат основой для расширения функциональности тем в окнах WPF.
Более общее определение темы - любая конфигурация внешнего вида на любом уровне детализации (ОС, приложение, управление). Когда люди используют общее определение, существует вероятность различной степени путаницы. Обратите внимание, что 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.
.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
Кроме того, 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 "
Загрузка тем в качестве скинов
Следует (PresentationFramework.Aero
ссылка) обновиться до версии 4.0?
Совершенно определенно. DataGrid не существовал в .NET FCL до 4.0. Есть несколько способов подтвердить это, но наиболее интуитивно понятным является то, что, по вашему собственному признанию, вы ранее обращались к нему через WPF Toolkit. Если вы решите не загружать PresentationFramework.Aero 4.0 в App.xaml, стиль Aero DataGrid не будет в ресурсах приложения.
Теперь очередьЭто даже не имеет значения.Я буду использовать исходный XAML, ломать отладчик при загрузке и проверять ресурсы области приложения.
Как и ожидалось, в свойстве 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.
Как вы правильно поняли, Aero 4.0 должен быть загружен в любом случае.Просто полезно знать, что происходит во время отладки.
Проблема № 1: Стиль DataGrid Aero не используется
DataGrid в этом приложении будет иметь ноль илибольше стилей, связанных в зависимости от того, как вы настраиваете свойства Style.BasedOn.
Он также будет иметь стиль по умолчанию, который, в вашем случае, встроен в тему Luna.
Я знал, просто взглянув на исходный XAML, возникла проблема наследования стиля.Большой стиль DataGrid с ~ 20 сеттерами не устанавливает его свойство BasedOn.
У вас есть цепочка стилей длины два, и ваш стиль по умолчанию взят из темы 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.Обратите внимание, что стиль по умолчанию не представлен в этой цепочке.
На данный момент следует отметить, что на самом деле нет изменений между стилями DataGrid каждой темы.Вы можете убедиться в этом, взяв стили DataGrid из соответствующих им тем 4.0 , скопировав их в отдельные текстовые файлы, а затем сравнив их с помощью инструмента сравнения.
На самом деле умеренное онемениеЭти стили просто идентичны от темы к теме. Это хорошо, чтобы быть в курсе. Чтобы убедиться в этом, просто запустите diff для всего XAML в двух разных темах.
Обратите внимание, что в 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). Обратите внимание, что стили темы, конечно же, также будут стилями по умолчанию!
Теперь допустим, что последние стили в цепочках стилей 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 делает то же самое, поэтому я не буду его освещать.)
Последний метод, DataGridHelper.GetCoercedTransferPropertyValue, по сути, сравнивает источник DataGridColumnHeader.Style и DataGrid.ColumnHeaderStyle.Какой источник имеет более высокий приоритет, побеждает.Правила приоритета в этом методе основаны на Свойство зависимости предшествования .
На этом этапе DataGrid.ColumnHeaderStyle будет проверяться как в исходном XAML, так и в решении XAML.Небольшая матрица информации будет собрана.В конечном счете это объяснит наблюдаемое поведение в каждом приложении.
В исходном XAML я ломаю отладчик и вижу, что DataGrid.ColumnHeaderStyle имеет источник 'Style'.Это имеет смысл, как это было установлено внутри стиля.
В решении XAML я ломаю отладчик и вижу, что DataGrid.ColumnHeaderStyle имеет «Default»источник.Это имеет смысл, так как это значение не было установлено в стиле (или где-либо еще).
Другое проверяемое значение - DataGridColumnHeader.Style.DataGridColumnHeader - глубоко вложенный элемент, который недоступен при отладке в VisualStudio.Реально, инструмент, такой как Snoop или WPF Inspector, будет использоваться для проверки свойства.
С исходным XAML, DataGridColumnHeader.Style имеет источник ImplicitStyleReference.Это имеет смысл.DataGridColumnHeaders создаются глубоко внутри внутреннего кода WPF.Их свойство Style имеет значение null, поэтому они будут искать неявный стиль.Дерево передается от элемента DataGridColumnHeader к корневому элементу.Как и ожидалось, стили не найдены.Затем ресурсы приложения проверяются.У вас есть строковый ключ («DataGrid_ColumnHeaderStyle»), установленный в одиночном стиле DataGridColumnHeader.Это эффективно скрывает это в этом поиске и поэтому не используется.Затем выполняется поиск обложки Aero и обнаруживается типичный неявный стиль.Это стиль, который используется.
Если этот шаг повторяетсяс решением XAML, результат тот же: 'ImplicitStyleReference'. На этот раз, однако, неявный стиль - это единственный стиль DataGridColumnHeader в DataGridResourceDictionary.xaml, теперь неявно имеющий ключ.
Наконец, если этот шаг повторяется еще раз с исходным XAML, , и обложка Aero не загружается , результат теперь будет «По умолчанию»! Это связано с тем, что во всем приложении просто не существует неявных стилей DataGridColumnHeader.
Таким образом, DataGrid.ColumnHeaderStyle будет использоваться, если обложка Aero не загружена, но не будет использоваться, если обложка Aero загружена! Как сообщалось, загрузка ресурсов темы может вызвать неблагоприятные последствия .
Это много, чтобы быть прямым, и все имена звучат одинаково. Следующая диаграмма повторяет все действия. Помните, что свойство с более высоким приоритетом выигрывает.
Возможно, это не то, что вам нужно, но именно так работает DataGrid в WPF 4.0. Принимая это во внимание, можно теоретически установить DataGrid.ColumnHeaderStyle и DataGrid.CellStyle в очень широкой области, и при этом иметь возможность переопределять стили DataGridColumnHeader и DataGridCell в более узкой области, используя неявные стили.
Опять же DataGrid.ColumnHeaderStyle и DataGrid.CellStyle превалируют над неявными стилями DataGridColumnHeader и DataGridCell. То есть они несовместимы со скином Aero. Таким образом, они просто удаляются из решения XAML.
Проблема № 3: DataGridRow.Background
Если до этого момента были выполнены рекомендуемые изменения, на экране должно быть что-то похожее на следующее. (Имейте в виду, что для устранения этой проблемы я установил для своей темы Классическую.)
DataGrid имеет вид Aero, но AlternatingRowBackground не соблюдается. У каждого второго ряда должен быть серый фон.
Используя методы отладки, обсуждавшиеся до сих пор, мы обнаружим, что это точно такая же проблема, как и проблема # 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».
Установите для параметра «Растянуть», и он будет работать как положено.
Существует некоторое взаимодействие между свойствами форматирования и TextBlock форматирования. Snoop и WPF Inspector позволяют экспериментировать и позволяют легко определить, что работает в любой конкретной ситуации.
Заключительные мысли
Подводя итог, загрузка ресурсов темы - это не то же самое, что изменение темы на уровне ОС. Загрузка ресурсов темы может привести к неблагоприятным последствиям. С точки зрения WPF, в приложении теперь присутствует большое количество неявных стилей. Эти стили могут превосходить другие стили. Суть в том, что такая тема может выглядеть как обложка приложения, может не работать без уточнений.
Тем не менее, я не полностью продан в текущей реализации WPF в отношении "вспомогательных свойств" (например, DataGrid.ColumnHeaderStyle), используемых посредством обратного вызова с правилами приоритета. Я должен задаться вопросом, почему они не могут быть просто локально назначены их предполагаемым целям (например, DataGridColumnHeader.Style) во время инициализации, если цели еще не имеют явно назначенного значения. Я не задумывался об этом, чтобы знать, какие могут быть различные проблемы, но если это возможно, это может сделать модель «свойства помощника» более интуитивной, более согласованной с другими свойствами и более надежной.
Наконец, хотя это и не было целью этого ответа, очень важно отметить, что загрузка ресурсов темы для имитации смены темы особенно плоха, поскольку существует существенная стоимость обслуживания . Существующие стили в приложении не будут автоматически основываться на стилях внутри ResourceDictionary темы. Каждый Стиль в приложении должен был бы установить свое свойство BasedOn для ключа Типа (или основываться, прямо или косвенно, на другом Стиле, который это делает). Это чрезвычайно обременительно и подвержено ошибкам. Кроме того, стоимость настраиваемых элементов управления с учетом тем зависит от стоимости. Ресурсы темы для этих пользовательских элементов управления также должны быть загружены для выполнения этой симуляции. И, конечно же, после этого у вас могут возникнуть проблемы с приоритетом стиля, подобные тем, с которыми вы столкнулись здесь!
В любом случае, существует несколько способов создания обложки для приложения WPF (без каламбура!). Я надеюсь, что этот ответ поможет вам лучше понять вашу проблему и поможет вам и другим решить аналогичные проблемы.