Главная » Silverlight » Модель частей и состояний

0

Используемая в предыдущем примере кнопка — всего лишь красный прямоугольник со скругленными углами. При наведении на нее указателя ее внешний вид не изменяет­ся. Конечно, она генерирует событие Click, но это слабое утешение. В WPF эту пробле­му можно решить с помощью триггеров, однако в Silverlight триггеры не поддержива­ются. Поэтому для изменения внешнего вида кнопки нужно добавить в ее шаблон спе­циальные именованные элементы и анимацию.

Для ознакомления с принципами кодирования шаблонов необходимо изучить до­кументацию Silverlight. Откройте страницу www.msdn.microsoft.com/en-us/library/ cc278075 (VS. 95) . aspx и выберите раздел, посвященный стилям и шаблонам. В нем вы можете ознакомиться с подробностями встроенных шаблонов каждого элемента управ­ления. Проблема состоит лишь в том, что коды шаблонов огромны и для ознакомления с ними нужно много времени.

Шаблон можно разбить на небольшие компоненты, однако для этого нужно пони­мать модель частей (parts) и состояний (states), используемую для организации ша­блонов Silverlight. Часть — это именованный элемент шаблона, ожидаемый элементом управления. Состояние — это именованная анимация, применяемая в заданное время.

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

Как узнать, какие части и состояния должны быть предоставлены шаблоном эле­мента управления обязательно? Есть два способа решения этой проблемы. Во-первых, можно ознакомиться с документацией Silverlight. На каждой странице, посвященной конкретному элементу управления, все необходимые части и состояния перечислены отдельно в двух таблицах. На рис. 13.3 показана таблица состояний элемента Button. Как и для многих других элементов управления, для элемента Button необходим ряд со­стояний, но не требуются части. Поэтому на странице приведена только одна таблица.

Во-вторых, для ознакомления с классом элемента управления можно применить рефлексию кода. Каждая часть представлена в объявлении класса отдельным атрибу­том TemplatePart, а каждое состояние — атрибутом TemplateVisualState. В следующих разделах эти атрибуты рассматриваются подробнее.

Рис. 13.3. Именованные состояния класса Button

Состояния кнопки

Если вы посмотрите в объявление класса Button (или его документацию, показан­ную на рис. 13.3), то увидите, что для создания кнопки нужно предоставить шесть со­стояний.

[TemplateVisualState(Name="Normal",

GroupName="CommonStates")] [TemplateVisualState(Name="MouseOver",

GroupName="CommonStates")] [TemplateVisualState(Name="Pressed",

GroupName="CommonStates")] [TemplateVisualState(Name="Disabled",

GroupName="CommonStates")] [TemplateVisualState(Name="Unfocused",

GroupName="FocusStates")] [TemplateVisualState(Name="Focused",

GroupName="FocusStates")]

public class Button : ButtonBase { … }

Состояния объединены в группы. Каждая группа содержит взаимно исключающие состояния, т.е. элемент управления может находиться только в одном из состояний группы. Например, для кнопки определены две группы: CommonStates и FocusStates. В каждый момент времени кнопка может находиться в одном из состояний группы CommonStates и в одном из состояний группы FocusStates.

Когда кнопка приобретает фокус в результате нажатия клавиши <Tkb>, она получа­ет состояние Normal (группы CommonStates) и Focused (группы FocusStates). Если по­сле этого на кнопку навести указатель, она получит состояния MouseOver и Focused. Группирование состояний облегчает управление ими. Без группирования пришлось бы вводить приоритеты состояний (например, чтобы выход из состояния MouseOver не приводил к потере фокуса) или создавать дополнительные состояния (например, FocusedNormal, UnfocusedNormal, FocusedMouseOver, UnfocusedMouseOver и т.д.).

Для определения групп состояний нужно добавить группу VisualStateManager. VisualStates в корневой элемент шаблона элемента управления.

<ControlTemplate x:Key="ButtonTemplate" TargetType="Button"> <Grid>

<VisualStateManager.VisualStateGroups>

</VisualStateManager.VisualStateGroups>

<Border x:Name="ButtonBorder" BorderBrush="Orange" BorderThickness="3" CornerRadius="15">

<Border.Background> <SolidColorBrush x:Name="ButtonBackgroundBrush" Color="Red" /> </Border.Background>

<ContentPresenter … /> </Border> </Grid>

</ControlTemplate>

Для добавления элемента VisualStateManager в шаблон необходим контейнер. В нем будут находиться видимые компоненты элемента управления и невидимый объект VisualStateManager. Как и другие ресурсы (см. главу 2), элемент VisualStateManager определяет объекты (например, раскадровки), используемые элементом управления в заданные моменты времени.

Обычно на корневой уровень шаблона добавляют контейнер Grid. В примере с кноп­кой контейнер Grid содержит элемент VusualStateGroups, определяющий группы, и элемент Border, выводящий кнопку.

В элементе VisualStateGroups группы создаются с помощью именованных элемен­тов visualStateGroup. Для кнопки нужно определить две группы.

<Vi sualStateManager.Vi sualStateGroups> <VisualStateGroup x:Name="CammonStates">

</VisualStateGroup>

<VisualStateGroup x:Name="FocusStates">

</VisualStateGroup>

</VisualStateManager.VisualStateGroups>

После добавления элементов VisualStateManager и VisualStateGroup можно добав­лять объекты Visual State для каждого состояния. Добавить можно либо все состояния, объявленные в документации шаблонов и атрибутах TemplateVisualState, либо только используемые. Например, при создании кнопки с эффектом наведения указателя мож­но добавить только состояния MouseOver (которое создает эффект) и Normal (приводя­щее кнопку к исходному внешнему виду).

<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CoinmonStates"> <VisualState x:Name="MouseOver">

</VisualState>

<VisualState x:Name="Normal">

</VisualState>

</VisualStateGroup>

<VisualStateGroup x:Name="FocusStates">

</VisualStateGroup> </VisualStateManager.VisualStateGroups>

Каждому состоянию соответствует раскадровка с одной или несколькими анимаци­ями» Раскадровки запускаются в заданные моменты времени. Например, когда пользо­ватель наводит указатель на кнопку, запускается анимация, выполняющая одну из сле­дующих операций.

•       Вывод нового внешнего вида кнопки. Для этого нужно изменить свойство Opacity элемента в шаблоне.

•       Изменение формы или позиции кнопки. Для изменения позиции можно при­менить объект преобразований TranslateTransform (например, немного сме­стить кнопку, создавая впечатление, будто кнопка нажата). Объекты ScaleTrans­form и RotateTransform используются для небольшого изменения формы кнопки при наведении на нее указателя.

•       Изменение освещенности или цвета. Для этого необходима анимация, управ­ляющая кистью, используемой для прорисовки фона. Для изменения цвета мож­но применить простую анимацию ColorAnimation в кисти SolidBrush, однако для создания более сложных эффектов необходимо анимировать другие кисти. Например, можно изменить один из цветов градиентной кисти GradientBrush (это делает шаблон элемента управления, используемый по умолчанию) или сме­стить центральную точку градиента RadialGradientBrush.

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

На рис. 13.4 показана кнопка, в которой анимация настраивает состояние, изменяя цвет фона при наведении указателя.

Рис. 13.4. Анимационный эффект в поль­зовательском шаблоне кнопки

Ниже приведена разметка кнопки, показанной на рис. 13.4.

<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Duration="0:0:0" Storyboard.TargetName="ButtonBackgroundBrush" Storyboard.TargetProperty="Color" To="Orange" /> </Storyboard> </VisualState>

<VisualState x:Name="Normal"> <Storyboard> <ColorAnimation Duration="0:0:0" Storyboard.TargetName="ButtonBackgroundBrush" Storyboard.TargetProperty="Color" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>

При переключении элемента управления из состояния MouseOver в состояние Normal надстройка Silverlight присваивает ему исходные значения свойств. Благодаря тому, что эти подробности не нужно задавать явно, разметку можно сделать проще и чище.

Жесткое кодирование анимационных значений

В предыдущем примере значение цвета фона (Orange) жестко закодировано. Нужные параметры можно извлечь из других свойств и применить к анимации с помощью расширения TemplateBinding. Этот способ полезен при соз­дании шаблонов настраиваемых элементов управления, содержащих жестко закодированные параметры, такие как цвет, шрифт, ширина пустых полосок и т.д. Каждый шаблон создает определенный внешний вид элемента управления.

Однако при создании шаблонов для новых пользовательских элементов управления важна гибкость. Для этого нужно обеспечить возможность настройки внешнего вида элемента управления путем установки свойств. Желательно иметь возможность не создавать новый шаблон для изменения одного параметра. Создание настраиваемых шаблонов рас­сматривается далее.

Источник: Мак-Дональд, Мэтью. Silverlight 3 с примерами на С# для профессионалов. : Пер. с англ. —- М. : ООО «И.Д. Вильяме», 2010. — 656 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии