Триггеры

54

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

Триггеры являются еще одним примером такой направленности WPF. С помощью триггеров можно автоматизировать процесс внесения простых изменений в стили, каковой обычно требует написания рутинной логики обработки событий. Например, можно обеспечить реакцию на изменение значения свойства и соответствующим образом автоматически подстроить стиль.

Триггеры связываются со стилями через коллекцию Style.Triggers. Каждый стиль может иметь любое количество триггеров, а каждый триггер является экземпляром класса, унаследованного от System.Windows.TriggerBase. Ниже перечислены классы триггеров, доступные в WPF.

С использованием коллекции FrameworkElement.Triggers триггеры можно применять к элементам напрямую, без необходимости в создании стиля. Однако здесь имеется одно серьезное ограничение. Коллекция Triggers поддерживает только триггеры событий. (Никаких формальных оснований для этого ограничения нет; просто разработчики WPF не успели завершить данную функциональность и, скорее всего, сделают это в следующих версиях.)

Trigger

Это простейшая форма триггера. Он следит за изменением в свойстве зависимости и затем использует средство установки для изменения стиля

MultiTrigger

Похож на Trigger, но поддерживает проверку множества условий. Этот триггер вступает в действие, только если удовлетворены все заданные условия

DataTrigger

Этот триггер работает с привязкой данных. Он похож на Trigger, но следит за изменением в любых связанных данных

MultiDataTrigger

Этот триггер объединяет множество триггеров данных

EventTrigger

Это наиболее сложный триггер. Он применяет анимацию, когда возникает соответствующее событие

Простой триггер

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

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

Ниже показан триггер, который ожидает получения текстовым полем фокуса с клавиатуры и в этом случае устанавливает для нее рамку светло-синего цвета и красный текст:

<Style x:Key="FontTextBox">
       <Style.Setters>
                <Setter Property="Control.FontFamily" Value="Calibri"></Setter>
                <Setter Property="Control.FontSize" Value="15"></Setter>
                <Setter Property="TextBox.VerticalContentAlignment" Value="Center"></Setter>
       </Style.Setters>
       <Style.Triggers>
                <Trigger Property="Control.IsFocused" Value="True">
                    <Setter Property="Control.Foreground" Value="Red"></Setter>
                    <Setter Property="TextBox.BorderBrush" Value="LightBlue"></Setter>
                    <Setter Property="TextBox.BorderThickness" Value="2"></Setter>
                </Trigger>
       </Style.Triggers>
</Style>
Использование простого триггера

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

Чтобы понять, как это работает, необходимо вспомнить систему свойств зависимости. Триггер, по сути, является одним из множества поставщиков свойств, способных переопределять значение, которое возвращает свойство зависимости. Однако исходное значение (как бы оно не устанавливалось — локально или с помощью стиля) все равно сохраняется. Как только триггер перестает действовать, значение, которое использовалось до его срабатывания, снова становится доступным.

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

Например, рассмотрим следующие триггеры, которые подстраивают элемент управления в зависимости от того, находится ли на нем фокус, наводится ли на него курсор мыши и выполняется ли на нем щелчок:

<Style x:Key="BigFontButton">
   <Style.Setters>
     ...
   </Style.Setters>
   <Style.Triggers>
      <Trigger Property="Control.IsFocused" Value="True">
         <Setter Property="Control.Foreground" Value="DarkRed" />
      </Trigger>
      <Trigger Property="Control.IsMouseOver" Value="True">
         <Setter Property="Control.Foreground" Value="LightYellow" />
         <Setter Property="Control.FontWeight" Value="Bold" />
      </Trigger>
      <Trigger Property="Button.IsPressed" Value="True">
         <Setter Property="Control.Foreground" Value="Red" />
      </Trigger>
   </Style.Triggers>
</Style>

Очевидно, что курсор мыши может быть помещен на кнопку, на которой в текущий момент находится фокус. Это проблемы не представляет, поскольку данные триггеры предполагают изменение разных свойств. Но при выполнении щелчка на кнопке установить цвет переднего плана попытаются одновременно два триггера. Победит в этом случае триггер, который отвечает за свойство Button.IsPressed, поскольку он идет последним в списке. То, какой из триггеров сработает первым, роли не играет — например, WPF безразлично, что кнопка получает фокус перед выполнением на ней щелчка. Роль играет только порядок, в котором триггеры перечислены в коде разметки.

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

В результате получаются элементы управления, которые могут полностью настраиваться и реагировать на наведение курсора мыши, щелчки и другие события, изменяя какой-то аспект своего внешнего вида.

Чтобы создать триггер, срабатывающий только при соблюдении сразу нескольких условий, можно воспользоваться классом MutliTrigger. Этот класс имеет коллекцию Conditions, которая позволяет определять цепочки комбинаций свойств и значений.

Ниже показан пример применения форматирования только в случае наведения на кнопку и фокуса и курсора мыши:

<Style x:Key="BigFontButton">
   <Style.Setters>
      ...
   </Style.Setters>
   <Style.Triggers>
      <MultiTrigger>
         <MultiTrigger.Conditions>
            <Condition Property="Control.IsFocused" Value="True" />
            <Condition Property="Control.IsMouseOver" Value="True" />
         </MultiTrigger.Conditions>
         <MultiTrigger.Setters>
            <Setter Property="Control.Foreground" Value="DarkRed" />
         </MultiTrigger.Setters>
      </MultiTrigger>
   </Style.Triggers>
</Style>

Здесь порядок, в котором объявляются условия, значения не имеет, поскольку для изменения фона требуется только то, чтобы они все возвращали true.

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