Цель лекции: получить знания о стилях и шаблонах. Научить создавать и повторно использовать шаблон Control’а. Задавать шаблон через стиль.
В Windows Presentation Foundation существует очень четкое разделение между поведением Control'а и тем, как он выглядит. К примеру, поведение объекта класса Button состоит в том, чтобы реагировать на различные события по клику, но его вид может быть любым — вы можете сделать кнопку в виде стрелки, рыбы, или чего-либо еще, что подходит для вашего приложения. Переопределение отображения Control'а очень просто сделать стилями и шаблонами. В WPF стилизация и использование шаблонов относятся к набору функций (стилей, шаблонов, триггеров и раскадровок), позволяющих разработчикам и дизайнерам создавать визуально привлекательные эффекты, а также создавать целостный внешний вид своих продуктов. Несмотря на то, что разработчики и дизайнеры могут настроить внешний вид в масштабе приложений, для обслуживания и синхронного использования внешнего вида внутри приложений и между приложениями необходима строгая модель стилей и шаблонов. WPF предоставляет такую модель.
Другая функция модели стилизации WPF состоит в разделении представления и логики. Это означает, что дизайнеры могут создавать внешний вид приложения, используя только XAML, в то же самое время, когда разработчики работают над логикой программы, используя C# или Visual Basic.
Стиль – это совокупность значений свойств, которые можно все сразу применить к нужному элементу. В Silverlight стили позволяют разгрузить вашу XAML разметку путем вынесения деталей форматирования элемента в отдельный блок. Система Silverlight стилей играет ту же роль, что и стандарт каскадных таблиц стилей (CSS) в HTML-верстке. Как и CSS, стили в Silverlight позволяют вам определять базовый набор характеристик форматирования и использовать их в вашем приложении для обеспечения согласованности. Но есть и несколько важных ограничений. Например, вы не сможете применить один стиль к элементам разных типов или назначить автоматическое использование стиля. По этой причине стили кажутся несколько неудобными, хотя и являются одной из ключевых возможностей.
Представьте, что вам нужно стандартизировать шрифт и его цвет для всех кнопок страницы. Первым делом опишите объект Style, содержащий в себе все необходимые вам свойства. Поместите этот объект как ресурс (например, в секции UserControl.Resources, в которой можно хранить ресурсы, относящиеся к данной странице):
<UserControl.Resources>
<Style x:Key="BigButtonStyle" TargetType="Button">
...
</Style>
</UserControl.Resources>
У стиля, как и у всех ресурсов, имеется имя-ключ, по которому вы можете к нему обращаться. В нашем примере имя ключа – BigButtonStyle. (Обычно принято добавлять к именам стилей окончание "Style"). Кроме того, для любого Silverlight стиля должно быть задано поле TargetType, определяющее тип элементов, к которым может применяться данный стиль. В нашем случае стиль создается для форматирования кнопок (Button).
Наш объект Style содержит коллекцию сеттеров (
Перед вами стиль большой кнопки с белым текстом шрифта Georgia на темном фоне:
<UserControl.Resources>
<Style x:Key="BigButtonStyle" TargetType="Button">
<Setter Property="FontFamily" Value="Georgia" />
<Setter Property="FontSize" Value="40" />
<Setter Property="Foreground" Value="SlateGray" />
<Setter Property="Background" Value="Black" />
<Setter Property="Padding" Value="20" />
<Setter Property="Margin" Value="10" />
</Style>
</UserControl.Resources>
В некоторых случаях вы не сможете задать значение свойства, используя простой формат записи атрибута. Например, простая запись не позволяет создавать неоднородную кисть LinearGradientBursh или ImageBrush. В такой ситуации вы можете применить уже знакомый для XAML прием замещения атрибута каким-либо вложенным элементом. Вот пример:
<Style x:Key="BigButtonStyle" TargetType="Button">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="Blue"></GradientStop>
<GradientStop Color="Yellow" Offset="1"></GradientStop>
</LinearGradientBrush>
</Setter.Value>
</Setter>
...
</Style>
Каждому элементу Silverlight можно задать только один стиль (или ни одного). Стиль встраивается в элемент через свойство стиля элемента (которое определено в базовом классе FrameworkElement). Например, чтобы применить к кнопке заранее созданный стиль, вы должны указать ресурс стиля, как в этом случае:
<Button
Style="{StaticResource BigButtonStyle}"
Content="A Customized Button"/>
Стили задают исходный внешний вид элемента, но вы вправе перекрыть параметры, заданные в стиле. Допустим, вы используете стиль BigButtonStyle, а также явно устанавливаете другое значение свойству FontSize, тогда значение свойства FontSize, указанное внутри тега кнопки, перекрывает это же свойство, описанное в стиле. Конечно, в идеале вы не должны полагаться на такое поведение. Вместо этого рекомендуется создать больше стилей, чтобы вы могли описать при помощи стилей как можно больше деталей элемента. Это даст вам большую гибкость с расчетом на последующие изменения настроек пользовательского интерфейса с меньшими проблемами.
На рис. 4.1 показана страница с двумя кнопками, использующими стиль BigButtonStyle.
(рис 4.1) Повторное использование настроек с применением стиля
Система стилей имеет много плюсов. Она не только позволяет вам создавать группы настроек с четкими связями, но и делает вашу XAML разметку более компактной благодаря простому способу подключения этих настроек. Но самое главное, вы можете применять стили, не заботясь об их внутреннем содержимом. В предыдущем примере настройки шрифта были описаны внутри стиля BigButtonSyle. Если спустя какое-то время вы решите увеличить величину значений свойств Padding и Margin, вы сможете просто добавить к описанию стиля соответствующие сеттеры свойств. Тогда новые настройки стиля автоматически вступят в силу для всех кнопок, использующих этот стиль.
Замечание. Технически допустимо задавать стиль программно. Тем не менее, это действие может быть выполнено только один раз. Если вы попытаетесь задать стиль кнопке, которая уже его получила, получите исключение.
В предыдущем примере стиль описан на уровне определенной страницы и затем использован для двух кнопок той же страницы. Хотя такая практика наиболее распространена, этот вариант размещения стиля не единственно возможный.
Собственно говоря, вы не обязаны объединять стили с ресурсами. Вы можете, к примеру, определить стиль отдельной кнопки, описав набор атрибутов стиля прямо в самой кнопке, как показано здесь:
<Button Content="A Customized Button">
<Button.Style>
<Style TargetType="Button">
<Setter Property="FontFamily" Value="Georgia" />
<Setter Property="FontSize" Value="40" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="Black" />
</Style>
</Button.Style>
</Button>
И эта, очевидно, мало чем полезная конструкция, будет работать. В этом случае вы уже не сможете повторно применить стиль к другим кнопкам.
Будет более разумно, если вы решите описать стили в отдельном ресурсе. Если вы пожелаете создать более строго специализированные стили, вы можете описать их в ресурсах контейнера, например в StackPanel или в Grid (такие стили можно будет применять только к элементам, вложенным в этот контейнер). Более того, один и тот же стиль можно описать на нескольких уровнях сразу (в контейнере StackPanel, содержащем кнопку, и внутри страницы, содержащей этот StackPanel). В такой ситуации Silverlight следует стандартному процессу поиска имен: сначала он ищет в ресурсах текущего элемента, затем в контейнере содержащем этот элемент, затем в следующем контейнере уровнем выше, и т.д., пока не найдет стиль с соответствующим именем. Если вы хотите, чтобы стиль был доступен в любой части кода вашего приложения, опишите его в ресурсах приложения (в файле App.xaml), поиск в котором происходит в последнюю очередь.
Стили (Styles) позволяют изменять внешний вид элементов. Однако у стилей имеются ограничения на изменение свойств, описанных в классе элемента. Например, в обычной кнопке присутствуют различные видимые детали, которые вы не в состоянии изменить, поскольку они не представлены через свойства. Одна из них – тень кнопки, возникающая в момент нажатия мышью.
В Silverlight имеется куда более радикальный инструмент для настройки подобных вещей, называемый шаблонами (templates). Тогда как стили можно применять к любому Silverlight элементу, использование шаблонов ограничено Silverlight Control’ами, т.е. элементами, которые наследуются от класса Control, принадлежащего пространству имен System.Windows.Controls. У таких элементов имеется свойство Template, через которое вы можете задавать свой шаблон, и тем самым, перекрыть стандартное визуальное представление Control’а.
Например, изменив шаблон, используемый объектом Button, вы можете создать множество экзотических кнопок, что было бы немыслимым при использовании одних только стилей. Вы сможете создавать кнопки округлой или другой нестандартной формы, а также кнопки со специальным эффектом, привлекающим взгляд в момент наведения мышкой (свечения, увеличения или мерцания).
Шаблоны – это одна из наиболее сложных деталей в WPF, поэтому не удивительно, что в Silverlight все еще отсутствуют некоторые возможности. Более неожиданно то, что факт отсутствия этих возможностей создает условия, в которых Silverlight Control’ы вынуждены опираться на новый комплекс стандартов и использовать более продуманные правила проектирования шаблонов.
Каждый Control содержит встроенный набор правил, определяющий его отрисовку (в виде набора более простых элементов). Этот набор правил называется шаблоном Control’а (control template). Описывается он как блок XAML-разметки и применяется к Control’у через свойство "Template". Для примера давайте рассмотрим простую кнопку. Предположим, создавая пользовательский Control, вы пожелаете получить больше контроля над эффектами затенения и анимации кнопки. В этом случае первым делом нужно заменить существующий стандартный шаблон кнопки на свой собственный. Для того, чтобы создать шаблон кнопки, вам понадобится нарисовать свой бордюр кнопки, ее фон, а также предусмотреть размещение контента кнопки. На роль бордюра имеется несколько кандидатов, тут все зависит от того, какой корневой элемент вы выберите:
В следующем примере используется класс Border для сочетания закругленного оранжевого контура и броского красного фона с белым текстом:
<Button Content="A Custom Button Template">
<Button.Template>
<ControlTemplate TargetType="Button" >
<Border BorderBrush="Orange" BorderThickness="3"
CornerRadius="10" Background="Red">
<TextBlock Foreground="White" Text="A Templated Button"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
На рисунке 4.2 вы видите результат:
(рис 4.2) Очень простая кнопка
Если вы попробуете использовать эту кнопку, то обнаружите, что данный шаблон слишком примитивен. Он лишен большей части признаков нормальной кнопки (например, визуального эффекта вдавливания в момент нажатия), и фактически игнорирует любое задаваемое свойство кнопки, даже самое основное - "Content". (Наш пример содержит жестко "вшитый" текст в TextBlock, так что команда "Content="A Custom Button Template"" тут только для вида.) Тем не менее, этот шаблон имеет все шансы стать настоящим шаблоном кнопки, и мы будем заниматься его доводкой в следующих параграфах.
Примечание: возможно как раз сейчас вы задумались, почему мы начали создание пользовательского шаблона, даже не взглянув на стандартный шаблон кнопки. Дело в том, что стандартные шаблоны содержат слишком много деталей: шаблон Control’а простой кнопки занимает целых четыре печатных листа. Поэтому только после того, как вы разберетесь в устройстве простого шаблона, вы сможете понять все детали стандартного шаблона.
В предыдущем примере описание шаблона расположено внутри элемента. Однако гораздо чаще шаблон Control’а размещают отдельно, т.к. часто вы будете применять шаблон сразу к нескольким Control’ам. Шаблон можно поместить в ресурсы:
<UserControl.Resources>
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button" >
<Border BorderBrush="Orange" BorderThickness="3" CornerRadius="10"
Background="Red">
<TextBlock Foreground="White" Text="A Custom Template"/>
</Border>
</ControlTemplate>
</UserControl.Resources>
Затем в теге кнопки дописать ссылку на StaticResource:
<Button Template="{StaticResource ButtonTemplate}"/>
Такой подход не только упростит создание множества специализированных кнопок, но и позволит вам в будущем свободно модифицировать шаблон Control’а без необходимости изменять каждую кнопку, тем самым, уменьшая риск что-то испортить.
Есть еще один вариант – вы можете вложить описание шаблона в описание стиля. Преимущество такого подхода в том, что стиль может сочетать сеттеры, устанавливающие другие свойства, с сеттером, определяющим новый шаблон Control’а. Применяя стиль к кнопке, вы задействуете все сеттеры. В результате ваша кнопка получит новый шаблон, а также все упомянутые в стиле значения свойств.
Кнопка из предыдущего примера довольно бесполезна, т.к. отображает только "вшитый" текст. Вы, конечно же, захотите иметь возможность задавать контент кнопки через свойство Button.Content. Для этого вам понадобится метка-заполнитель специального назначения – "ContentPresenter".
ContentPresenter присутствует во всех Control’ах, содержащих какой-либо контент. Это специальная метка означающая "вставь контент здесь" и говорящая Silverlight, куда следует поместить содержимое Control’а. Вот как вы можете применить ее в нашем примере:
<UserControl.Resources>
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button" >
<Border BorderBrush="Orange" BorderThickness="3" CornerRadius="10"
Background="Red">
<ContentPresenter/>
</Border>
</ControlTemplate>
</UserControl.Resources>
Замечание: ContentPresenter – наиболее востребованная, но не единственная метка. Control’ы представляющие списки и использующие ItemsControl применяют в своих шаблона метку ItemsPresenter, определяющую место, где должна располагаться панель списка элементов. Прокручиваемый контент Control’а ScrollViewer представлен меткой ScrollContentPresenter.
Несмотря на то, что наш улучшенный шаблон кнопки уже поддерживает свойство контента, он все еще не воспринимает большинство других свойств. Для примера давайте рассмотрим следующий случай использования шаблона:
<Button Template="{StaticResource ButtonTemplate}"
Content="A Templated Button"
Margin="10"
Padding="20"/>
В этой разметке свойствам кнопки Margin и Padding заданы значения 10 и 20 соответственно. За учет свойства Margin отвечает контейнер, содержащий кнопку, поэтому с этим проблем не возникает. Но свойство Padding не берется в расчет, поэтому края кнопки вплотную прилегают к ее контенту. Дело в том, что свойство Padding не будет иметь никакого эффекта, пока вы сами не позаботитесь об этом. Иначе говоря, именно ваш шаблон должен позаботиться о дополнительных отступах вокруг контента кнопки, величина которых задается в Padding.
Для этой цели в Silverlight\WPF есть специальная возможность – связывание в шаблонах (template bindings). С помощью template binding шаблон Control’а сможет извлекать значения свойств Control’а, использующего данный шаблон. В следующем примере вы можете использовать template binding для получения значения свойства Padding и создания отступа вокруг ContentPresenter:
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button" >
<Border BorderBrush="Orange"
BorderThickness="3"
CornerRadius="10"
Background="Red">
<ContentPresenter Margin="{TemplateBinding Padding}"/>
</Border>
</ControlTemplate>
Теперь желаемый эффект получен и установлен некоторый зазор между текстом и краями кнопки. рис. 4.3 демонстрирует вашу новую скромную кнопку:
(рис 4.3) Кнопка пользовательского шаблона Control’а
Замечание: Связывание в шаблонах похоже на обычное связывание данных (
Стало быть, вам придется добавить в ContentPresenter приличное количество элементов, если вы, конечно, хотите получить полноценную поддержку свойств класса Button и иметь возможность задавать такие свойства, как выравнивание, ControlPresenter в составе стандартного шаблона кнопок выглядит примерно так:
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
Связывание в шаблонах очень важно для свойства Content. Благодаря связыванию содержимое извлекается из Control’а и отображается в ContentPresenter. Зачастую можно не включать связывание для некоторых свойств шаблона, если вы не намерены их использовать.
Замечание: Связывание в шаблонах поддерживает встроенную во все зависимые свойства инфраструктуру мониторинга изменений. Это значит, что если вы изменяете свойство Control’а, шаблон автоматически применяет его новое значение. Это особенно полезно, когда вы используете анимацию, многократно изменяющую значение свойства.
Связывание в шаблонах не ограничивается классом ContentPresenter. На самом деле вы можете использовать его в любом месте шаблона Control’а. Давайте рассмотрим наш пример кнопки с "вшитым" красным цветом фона элемента Border. Вот как можно использовать шаблонное связывание для управления фоном:
<Border BorderBrush="Orange"
BorderThickness="3"
CornerRadius="10"
Background="{TemplateBinding Background}">
Этот пример затрагивает один из вечных вопросов проектирования. Стоит ли жестко "зашивать" цвет, чтобы иметь стандартный вид кнопок, или применить связывание, чтобы сделать шаблон более гибким? На этот раз у нас имеется отличный компромисс, позволяющий убить сразу двух зайцев: вы можете сочетать шаблоны и стили! Основная идея в том, чтобы шаблон позволял изменять цвет фона, а стиль содержал бы значения по умолчанию, на случай, если цвет не будет указан явно:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="Orange"
BorderThickness="3"
CornerRadius="10"
Background="{TemplateBinding Background}">
<ContentPresenter Margin="{TemplateBinding Padding}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Но можно оставить стиль отдельным ресурсом:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Setter Property="Template" Value="{StaticResource ButtonTemplate}"/>
</Style>
Только учтите, что в таком случае объявление стиля должно следовать после объявления шаблона.
Синхронное использование стилей и шаблонов также бывает полезным, если вам нужно задать свойства (зависимые свойства), отсутствующие в ContentPresenter или в элементах контейнера вашего шаблона. Обратите внимание, что в нашем примере нет связывания для цвета шрифта кнопки. А все потому, что эти свойства (Foreground, FontFamily, FontSize, FontWeight, и т.д.) участвуют в наследовании свойств. Когда вы задаете эти значения в элементе более высокого уровня (например, в кнопке), они распространяются на вложенные элементы (например, TextBlock внутри кнопки). Сам же ContentPresenter не имеет этих свойств, и в этом нет необходимости. Значения этих свойств передаются от Control’а к вложенному контенту минуя ContentPresenter.
Иногда вам может понадобиться изменить значение наследуемого свойства, чтобы ограничить изменение вида вашего Control’а. Например, в нашем случае важно, чтобы цвет текста был белым, поскольку белый текст лучше выделяется на красном фоне кнопки. Но стандартный цвет шрифта наследуется от Silverlight страницы, на которой расположены наши элементы, и этот цвет – черный. К тому же вы не можете установить цвет через ContentPresenter, т.к. он не содержит свойства Foreground. Для решения этой проблемы нужно объединить шаблон Control’а со стилем, сеттер которого задает белый цвет текста:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="Red"/>
<Setter Property="Template" Value="{StaticResource ButtonTemplate}"/>
</Style>
Данный подход объединяет в себе удобство и гибкость. Без каких либо дополнительных действий вы автоматически получаете кнопку с красным фоном и белым текстом, но также можете создать другой стиль, задающий другую
Как вы уже знаете, гибкий шаблон Control’а можно настраивать через свойства Control’а, значение которых можно задавать в стиле. Однако Silverlight приложения редко изменяют свойства только одного Control’а за раз. Как правило, для изменения вида приложения изменяются настройки сразу у множества шаблонов. В таких ситуациях нужно уметь синхронно использовать определенные свойства Control’ов (например, настройки цвета). Для этого все "вшитые" значения вынесите из стилей и шаблонов, описав их как отдельные ресурсы. Например:
<SolidColorBrush x:Key="BackgroundBrush" Color="Red"/>
Затем вы можете обращаться к этим ресурсам из стилей и шаблонов:
<Style x:Key="ButtonStyle" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="{StaticResource BackgroundBrush}"/>
<Setter Property="Template" Value="{StaticResource ButtonTemplate}"/>
</Style>
Простое добавление ресурса с определенным именем позволит вам использовать один и тот же шаблон, но различные цвета бордюра. Однако имеется и слабая сторона такого подхода, т.к. это несколько усложняет ваш проект.
Для большей гибкости вы можете вынести в отдельный ресурс даже настройки цветов, а затем использовать их как ресурсы кисти:
<Color
x:Key="BackgroundColor">#FF800000</Color>
<SolidColorBrush
x:Key="BackgroundBrush" Color="{StaticResource BackgroundColor}"/>
Такой принцип позволяет повторно использовать
Замечание: при объявлении цвета как отдельного ресурса ему может быть задан цвет в виде имени цвета или в виде шестнадцатеричного HTML-кода цвета (как в примере). Но, к сожалению, нельзя объявить цвет в XAML, используя набор красного, зеленого, и синего составляющих цвета.
Styles: совокупность значений свойств, которые можно все сразу применить к нужному элементу.
Templates: каждый Control содержит встроенный набор правил, определяющий его отрисовку (в виде набора более простых элементов). Этот набор правил называется шаблоном Control’а (control template). Описывается он как блок XAML-разметки и применяется к Control’у через свойство "Template".
В этой лекции мы рассмотрели механизм применения некоторого набора свойств к одному или нескольким элементам управления - стили. С помощью стилей можно создавать однородные темы и применять их к разным приложениям. Однако у стилей имеются ограничения на изменение свойств, описанных в классе элемента. В Silverlight имеется куда более радикальный инструмент для настройки подобных вещей, называемый шаблонами (templates). Тогда как стили можно применять к любому Silverlight элементу, использование шаблонов ограничено Silverlight Control’ами, т.е. элементами, которые наследуются от класса Control, принадлежащего пространству имен System.Windows.Controls. Более детально эти отличия мы рассмотрим в следующей лекции.
Вопросы:
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.