Визуальные состояния

28

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

Преимущества этой модели в ее исключительной мощи и гибкости. Можно изменять все, что требуется. Это не сразу видно на примере с кнопкой, потому что там шаблон элемента управления полагается на встроенные свойства вроде IsMouseOver и IsPressed. Но даже если бы эти свойства были недоступны, все равно можно было бы построить шаблон элемента управления, который изменяет себя в ответ на перемещения курсора мыши и щелчки.

Трюк состоит в использовании триггеров событий, применяющих анимации. Например, можно добавить триггер события, который реагирует на Border.MouseOver запуском анимации, изменяющей цвет фона границы. Эта анимация даже не обязана выглядеть как анимация: если установить для нее длительность в 0 секунд, она произойдет мгновенно, как в случае изменения свойства, используемого сейчас. Фактически именно такой прием применяется во многих примерах профессиональных шаблонов.

Несмотря на свои возможности, шаблоны на основе триггеров обладают одним недостатком: они требуют от проектировщика шаблона детального понимания работы элемента управления. Так, в примере с кнопкой проектировщик шаблона должен знать о существовании свойств IsMouseOver и IsPressed, а также о том, как их использовать. И это еще не все — например, большинство элементов управления должно визуально реагировать на перемещения курсора мыши, позволять отключение, получать фокус и поддерживать многие другие изменения состояния. Когда все эти состояния применяются в сочетании, может быть нелегко определить точно, как должен выглядеть элемент управления.

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

В WPF 4 появилось новое средство, именуемое визуальными стилями (visual style), которое призвано справиться с решением этой задачи. Используя именованные части (уже показанные ранее) и визуальные стили, элемент управления может предоставлять стандартизованный визуальный контракт. Вместо знания устройства всего элемента управления проектировщик шаблона должен лишь понять правила визуального контракта. В результате намного легче становится спроектировать простой шаблон элемента управления, особенно когда он предназначен для элемента, с которым не приходилось работать ранее.

Подобно тому, как элементы могут использовать атрибут TemplatePart для указания специфических именованных элементов (или частей), которые должен включать в себя шаблон элемента управления, они могут применять атрибут TemplateVisualState для указания поддерживаемых визуальных стилей. Например, обычная кнопка может предоставить примерно следующий набор визуальных состояний:

[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.

Например, если переход на кнопку был совершен клавишей <Tab>, ее состоянием будет Normal (из CommonStates) и Focused (из FocusStates). Без групп состояний было бы нелегко справиться с этой ситуацией. Пришлось бы либо определять некоторое преимущество одних состояний над другими (так что кнопка в состоянии MouseOver теряла бы индикатор фокуса), либо создавать намного больше состояний (таких как FocusedNormal, UnfocusedNormal, FocusedMouseOver, UnfocusedMouseOver и т.д.).

VSM добавлен в WPF в качестве обратной совместимости с проектами Silverlight, в которых нет поддержки триггеров. Поэтому более подробно о диспетчере состояний рассказывается в теме Visual State Manager в разделе по Silverlight.

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