Множество уровней стилей

39

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

Например, предположим, что группе элементов управления требуется назначить один и тот же шрифт без применения к каждому из них одного и того же стиля. В этом случае можно разместить нужные элементы управления в одной панели (или в контейнере другого типа) и установить стиль контейнера. При условии установки свойств, которые используют средство наследования значений, эти значения будут передаваться дочерним элементам. К числу свойств, которые поддерживают такую модель, относятся IsEnabled, IsVisible, Foreground и все свойства, связанные со шрифтом.

В других ситуациях требуется создать стиль, основанный на другом стиле. Для использования наследования стилей необходимо установить атрибут BasedOn соответствующего стиля. Например, рассмотрим два следующих стиля:

<Style x:Key="MyButtonStyle">
            <Setter Property="Control.FontFamily" Value="Calibri"></Setter>
            <Setter Property="Control.FontSize" Value="18"></Setter>
            <Setter Property="Control.FontWeight" Value="Bold"></Setter>
            <Setter Property="Control.Padding" Value="5"></Setter>
            <Setter Property="Control.Margin" Value="5"></Setter>
</Style>
<Style x:Key="TiledStyle" BasedOn="{StaticResource MyButtonStyle}">
           <Setter Property="Control.Background">
                <Setter.Value>
                    <ImageBrush TileMode="Tile" ViewportUnits="Absolute"
                                Viewport="0 0 32 32" ImageSource="cry.png"
                                Opacity="0.3"></ImageBrush>
                </Setter.Value>
            </Setter>
</Style>

Первый стиль (MyButtonStyle) определяет три свойства шрифта и два свойства для отступов. Второй стиль (TiledStyle) получает их от MyButtonStyle и затем дополняет свойством, которое изменяет кисти фона. Такое состоящее из двух частей проектное решение предоставляет возможность применять одни только настройки шрифта либо комбинацию настроек шрифта и цвета. Это решение также позволяет создавать больше стилей, включающих в себя уже определенные детали шрифта или цвета (причем не обязательно вместе).

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

На рисунке показан пример работы наследования стилей в простом окне, использующем оба стиля:

Создание стиля основанного на другом стиле

Хотя наследование стилей на первый взгляд выглядит очень удобным приемом, обычно оно не стоит затрачиваемых усилий. Причина в том, что наследование стилей влечет за собой те же проблемы, что и наследование кода, т.е. приводит к образованию зависимостей, которые делают приложение более хрупким. Например, в случае использования приведенной выше разметки одни и те же характеристики шрифта обязательно должны сохраняться для обоих стилей. Модификация стиля MyButtonStyle приводит также к изменению стиля TiledStyle — если только явно не добавить дополнительные средства установки, переопределяющие унаследованные значения.

Эта проблема тривиальна в примере с двумя стилями, но существенно усложняется при наследовании стилей в реальных приложениях. Обычно стили делятся на категории на основе типов содержимого и ролей, которые это содержимое исполняет. Например, приложение для продаж может включать в себя стили наподобие ProductTitleStyle, ProductTextStyle, HighlightQuoteStyle, NavigationButtonStyle и т.д. Если основать стиль ProductTitleStyle на ProductTextStyle (возможно потому, что они оба разделяют один и тот же шрифт), то возникнут проблемы, когда позже понадобится применить к стилю ProductTextStyle настройки, которые не должны применяться к стилю ProductTitleStyle (например, разные поля). В таком случае придется определить эти настройки в стиле ProductTextStyle и явно переопределить их в стиле ProductTitleStyle. В конечном итоге это приведет к получению гораздо более сложной модели и весьма небольшому количеству настроек, которые действительно используются многократно.

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

Пройди тесты