Ресурсы приложения и системы

95

Ресурсы приложения

Элемент Window не является последним местом поиска ресурса. Если указан ресурс, который не удается найти ни в элементе управления, ни в одном из его контейнеров (вплоть до окна или страницы, содержащей этот элемент), WPF продолжает проверку в наборе ресурсов, которые были определены для приложения. В Visual Studio таковыми являются ресурсы, которые были определены в разметке внутри файла App.xaml:

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ImageBrush x:Key="TileBrush" TileMode="FlipX" ViewportUnits="Absolute"
                    Viewport="0 0 32 32" ImageSource="big_smile.png" Opacity="0.4"></ImageBrush>
    </Application.Resources>
</Application>

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

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

Ресурсы приложения рекомендуется создавать, если объект многократно используется повсеместно в приложении (например, во множестве окон). Если же он используется только в двух или трех местах, тогда лучше рассмотреть вариант с определением ресурса в каждом окне.

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

Ресурсы системы

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

Секрет кроется в наборе из трех классов SystemColors, SystemFonts и SystemParameters, которые расположены в пространстве имен System.Windows. Класс SystemColors предоставляет доступ к настройкам цвета. Класс SystemFonts обеспечивает доступ к настройкам шрифтов. Класс SystemParameters охватывает огромный список настроек, которые описывают стандартный размер различных экранных элементов, параметры клавиатуры и мыши, размер экрана, а также активные графические эффекты (вроде отбрасывания теней и отображения содержимого окон при перетаскивании).

Классы SystemColors и SystemFonts доступны в двух версиях, которые размещены в пространствах имен System.Windows и System.Drawing. Версии из System.Windows являются частью WPF. Они используют правильные типы данных и поддерживают систему ресурсов. Версии из System.Drawing относятся к Windows Forms. В приложениях WPF они бесполезны.

Все детали в классах SystemColors, SystemFonts и SystemParameters предоставляются через статические свойства. Например, свойство SystemColors.WindowTextColor дает структуру Color, которую можно использовать любым желаемым образом. Ниже приведен пример ее применения для создания кисти и заливки переднего плана элемента:

label.Foreground = SystemColors.WindowTextBrush;

Доступ к статическим свойствам в WPF можно получать с помощью статического расширения разметки. Например, ниже показано, как установить основной фон того же самого элемента Label в XAML-разметке:

<Label Foreground="{х:Static SystemColors.WindowTextBrush}">
   Ordinary text
</Label>

В этом примере не используется ресурс. Кроме того, с ним связан один небольшой недостаток: при выполнении синтаксического анализа окна и создании метки кисть создается на основании текущего "снимка" цвета текста в окне. В случае изменения цветов Windows во время работы приложения (после отображения окна, содержащего метку) метка не сможет обновиться. Приложения, которые ведут себя подобным образом, считаются несколько неаккуратными.

Решить эту проблему установкой свойства Foreground непосредственно в объект кисти нельзя. Вместо этого его понадобится установить в объект DynamicResource, служащий оболочкой для данного системного ресурса. К счастью, во всех классах SystemXxx предоставляется дополнительный набор свойств, которые возвращают объекты ResourceKey — ссылки, позволяющие извлекать ресурс из коллекции ресурсов системы. Эти свойства имеют те же имена, что и обычные свойства, напрямую возвращающие объект, со словом Key в конце. Например, ключом ресурса для SystemColors.WindowTextBrush будет SystemColors.WindowTextBrushKey.

Ключи ресурсов представляют собой не простые имена, а ссылки, которые сообщают WPF о том, где следует искать конкретный ресурс. Класс ResourceKey является непрозрачным, т.е. он не показывает низкоуровневые детали о том, как идентифицируются ресурсы системы. Однако переживать о возможных конфликтах между собственными ресурсами и ресурсами системы не стоит, поскольку они размещаются в отдельных сборках и трактуются по-разному.

Ниже показано, как использовать ресурс из класса SystemXxx:

<Label Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
Ordinary text
</Label>

Этот код разметки немного сложнее приведенного в предыдущем примере. Он начинается с определения динамического ресурса. Динамический ресурс, однако, не извлекается из коллекции ресурсов в приложении. Вместо этого применяется ключ, определенный свойством SystemColors.WindowTextBrushKey. Поскольку свойство является статическим, также должно использоваться статическое расширение разметки, чтобы анализатор смог понять намерения.

После внесения такого изменения метка будет без проблем обновляться в случае изменения настроек системы.

Пройди тесты
Лучший чат для C# программистов