Общие сведения о перенаправленных событиях

В этом разделе описывается понятие перенаправленных событий в Windows Presentation Foundation (WPF). Здесь определяется терминология перенаправленных событий, описывается, как перенаправленные события маршрутизируются через дерево элементов, кратко описываются способы обработки перенаправленных событий, а также способы создания пользовательских перенаправленных событий.

Предварительные требования

В этом разделе предполагается, что у вас есть базовые знания о среде CLR и объектно-ориентированном программировании, а также понятие о том, как отношения между WPF элементами можно представить в виде дерева. Чтобы выполнить примеры в этом разделе, следует также понимать Язык XAML и знать, как писать простые приложения или страницы WPF. Дополнительные сведения см. в разделе Пошаговое руководство. мое первое приложение WPF для настольных компьютеров и XAML в WPF.

Что такое перенаправленное событие?

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

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

Определение реализации: перенаправленное событие — это событие CLR, которое поддерживается экземпляром RoutedEvent класса и обрабатывается Windows Presentation Foundation (WPF) системой событий.

Обычно в приложении WPF содержится много элементов. В зависимости от того, созданы элементы в коде или объявлены в XAML, они связаны в дереве элементов друг с другом. Маршрут события может проходить в одном из двух направлений в зависимости от определения события, но обычно маршрут проходит от исходного элемента и затем "всплывает" вверх по дереву элементов до тех пор, пока не достигнет корневого элемента дерева (как правило, страница или окно). Концепция всплывания может быть знакома, если имеется опыт работы с моделью DHTML-объектов.

Рассмотрим следующее простое дерево элементов.

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Это дерево элементов выглядит примерно следующим образом.

Кнопки Да, нет и Отмена

В этом упрощенном дереве элементов источником Click события является один из Button элементов, а в зависимости от того, какой Button элемент был нажат, первым элементом, который имеет возможность справиться с событием. Но если к событию не прикрепляется обработчик, то событие будет перенаправлено вверх Button к Button родительскому элементу в дереве элементов, то есть StackPanel . Потенциально событие переносится в Border , а затем выходит в корень страницы дерева элементов (не показано).

Иными словами, маршрут события для этого Click события:

Button-->StackPanel-->Border-->...

Сценарии верхнего уровня для перенаправленных событий

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

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

Точки вложенности единственного обработчика: в Windows Forms необходимо несколько раз присоединить один обработчик к обработке событий, которые могут быть вызваны из нескольких элементов. Перенаправленные события позволяют присоединить обработчик только один раз, как было показано в предыдущем примере, и при необходимости использовать логику обработки для определения места возникновения события. Например, это может быть обработчиком для ранее показанного XAML.

private void CommonClickHandler(object sender, RoutedEventArgs e)
{
  FrameworkElement feSource = e.Source as FrameworkElement;
  switch (feSource.Name)
  {
    case "YesButton":
      // do something here ...
      break;
    case "NoButton":
      // do something ...
      break;
    case "CancelButton":
      // do something ...
      break;
  }
  e.Handled=true;
}
Private Sub CommonClickHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
  Dim feSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
  Select Case feSource.Name
    Case "YesButton"
      ' do something here ...
    Case "NoButton"
      ' do something ...
    Case "CancelButton"
      ' do something ...
  End Select
  e.Handled=True
End Sub

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

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

Реализация перенаправленных событий

Перенаправленное событие — это событие CLR, которое поддерживается экземпляром RoutedEvent класса и регистрируется в WPF системе событий. RoutedEventЭкземпляр, полученный из регистрации, обычно сохраняется как public static readonly элемент поля класса, который регистрирует и, таким образом, "владеет" перенаправленным событием. Соединение с событием CLR с одинаковым именем (которое иногда называется "оболочкой") достигается путем переопределения add remove реализаций и для события CLR. Как правило, add и remove остаются неявными по умолчанию и используют соответствующий синтаксис события определенного языка для добавления и удаления обработчиков события. Механизм резервного копирования и подключения перенаправленных событий концептуально похож на то, как свойство зависимостей является свойством CLR, которое поддерживается DependencyProperty классом и регистрируется в WPF системе свойств.

В следующем примере показано объявление пользовательского Tap перенаправленного события, включая регистрацию и раскрытие поля идентификатора, а RoutedEvent также add remove реализаций для Tap события CLR.

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
        add { AddHandler(TapEvent, value); }
        remove { RemoveHandler(TapEvent, value); }
}
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(MyButtonSimple))

' Provide CLR accessors for the event
Public Custom Event Tap As RoutedEventHandler
    AddHandler(ByVal value As RoutedEventHandler)
        Me.AddHandler(TapEvent, value)
    End AddHandler

    RemoveHandler(ByVal value As RoutedEventHandler)
        Me.RemoveHandler(TapEvent, value)
    End RemoveHandler

    RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Me.RaiseEvent(e)
    End RaiseEvent
End Event

Обработчики перенаправленных событий и XAML

Чтобы добавить обработчик для события с помощью XAML, следует объявить имя события как атрибут для элемента, который является прослушивателем событий. Значением атрибута является имя метода реализуемого обработчика, который должен существовать в разделяемом классе файла кода программной части.

<Button Click="b1SetColor">button</Button>

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

Стратегии маршрутизации

Перенаправленные события используют одну из трех стратегий маршрутизации.

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

  • Прямая маршрутизация. Только элемент-источник события имеет возможность вызывать обработчики событий. это аналогично «маршрутизации», которую Windows Forms использует для событий. Однако, в отличие от стандартного события CLR, прямые перенаправленные события поддерживают обработку классов (обработка классов объясняется в следующем разделе) и может использоваться EventSetter и EventTrigger .

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

Зачем использовать перенаправленные события?

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

Перенаправленные события являются мощным инструментом при использовании их в одном из предлагаемых сценариев: при определении общих обработчиков для общего корня, при композиции собственного элемента управления или при определении собственного класса пользовательского элемента управления.

Прослушиватели перенаправленных событий и источники перенаправленных событий не требуются для совместного использования общего события в их иерархии. Any UIElement или ContentElement может быть прослушивателем событий для любого перенаправленного события. Таким образом, можно использовать полный набор перенаправленных событий, доступных во всем рабочем наборе API в качестве концептуального "интерфейса", благодаря которому разнородные элементы в приложении могут обмениваться данными о событиях. Эта концепция "интерфейса" для перенаправленных событий особенно применима для событий ввода.

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

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

  • Некоторые WPF функции стилизации и создания шаблонов, такие как EventSetter и, EventTrigger занимают событие, на которое указывает ссылка, как перенаправленное событие. Это сценарий идентификатора события, упомянутого выше.

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

Каждое из описанных выше рассуждений рассматривается в отдельной части этого раздела.

Добавление и реализация обработчика событий для перенаправленного события

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

<Button Click="b1SetColor">button</Button>

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

void b1SetColor(object sender, RoutedEventArgs args)
{
  //logic to handle the Click event
}
Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
  'logic to handle the Click event
End Sub

RoutedEventHandler является базовым делегатом обработчика перенаправленных событий. Для перенаправленных событий, которые являются специализированными для определенных элементов управления или скриптов, делегаты, которые используются для обработчиков перенаправленных событий, также могут быть более специализированными, чтобы они могли передавать определенные данные события. Например, в общем сценарии ввода может быть обработано DragEnter перенаправленное событие. Обработчик должен реализовать DragEventHandler делегат. Используя наиболее конкретный делегат, можно обработать DragEventArgs в обработчике и прочитать Data свойство, которое содержит полезные данные буфера обмена операции перетаскивания.

Полный пример добавления обработчика событий к элементу с помощью XAML см. в разделе Обработка перенаправленных событий.

Добавить обработчик для перенаправленного события в приложении, созданном в коде, достаточно просто. Обработчики перенаправленных событий всегда можно добавлять с помощью вспомогательного метода AddHandler (который является тем же методом, для которого существуют резервные вызовы add .) Однако существующие WPF перенаправленные события обычно имеют резервные реализации add и remove логики, позволяющие добавлять обработчики перенаправленных событий с помощью синтаксиса событий конкретного языка, который является более интуитивным синтаксисом, чем вспомогательный метод. Ниже приведен пример использования вспомогательного метода.

void MakeButton()
 {
     Button b2 = new Button();
     b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
 }
 void Onb2Click(object sender, RoutedEventArgs e)
 {
     //logic to handle the Click event
 }
Private Sub MakeButton()
     Dim b2 As New Button()
     b2.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Onb2Click))
End Sub
 Private Sub Onb2Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
     'logic to handle the Click event     
 End Sub

в следующем примере показан синтаксис оператора C# (Visual Basic несколько отличается синтаксисом оператора из-за его обработки разыменованием):

void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event
}
Private Sub MakeButton2()
  Dim b2 As New Button()
  AddHandler b2.Click, AddressOf Onb2Click2
End Sub
Private Sub Onb2Click2(ByVal sender As Object, ByVal e As RoutedEventArgs)
  'logic to handle the Click event     
End Sub

Пример добавления обработчика событий в коде см. в разделе Добавление обработчика событий с помощью кода.

если вы используете Visual Basic, можно также использовать Handles ключевое слово для добавления обработчиков в составе объявлений обработчика. Дополнительные сведения см. в разделе Обработка событий в Visual Basic и WPF.

Концепция обработанных событий

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

Значение Handled влияет на то, как перенаправленное событие сообщается или обрабатывается, когда оно перемещается дальше вдоль маршрута. Если Handled параметр находится true в данных события для перенаправленного события, то обработчики, которые прослушивают перенаправленное событие для других элементов, обычно больше не вызываются для этого конкретного экземпляра события. Это справедливо как для обработчиков, присоединенных к XAML, так и для обработчиков, добавленных с помощью синтаксиса присоединения обработчика событий конкретного языка, например += или Handles. Для наиболее распространенных сценариев обработчика Пометка события как обработанного путем установки параметра Handled в значение true будет "прекращать" маршрутизацию для маршрута туннелирования или для восходящей маршрутизации, а также для любого события, которое обрабатывается в точке в маршруте обработчиком класса.

Однако существует механизм "handledEventsToo", при котором прослушиватели по-прежнему могут запускать обработчики в ответ на перенаправляемые события, где Handled находится true в данных события. Другими словами, маршрут события в действительности не останавливается при пометке события в данных события как обработанного. Механизм handledEventsToo можно использовать только в коде или в EventSetter :

  • В коде вместо использования синтаксиса событий, зависящего от языка, который работает для общих событий CLR, вызовите WPF метод, AddHandler(RoutedEvent, Delegate, Boolean) чтобы добавить обработчик. Задайте для handledEventsToo значение true.

  • В EventSetter присвойте HandledEventsToo атрибуту значение true .

В дополнение к поведению, которое Handled получается в перенаправленных событиях, концепция Handled имеет влияние на то, как следует проектировать приложение и написать код обработчика событий. Можно концептуализировать Handled как простой протокол, предоставляемый перенаправленными событиями. Именно вы используете этот протокол, но концептуальное проектирование того, как Handled предполагается использовать значение, выглядит следующим образом:

  • Если перенаправленное событие помечено как обработанное, то затем его не требуется снова обрабатывать другими элементами в маршруте.

  • Если перенаправленное событие не помечено как обработанное, то другие прослушиватели, ранее находились в маршруте, отказались от регистрации обработчика, или обработчики, которые были зарегистрированы, отказались от обработки данных события и присвоить параметру значение Handled true . (Или, конечно, возможно, что текущий прослушиватель является первой точкой маршрута.) Обработчики в текущем прослушивателе теперь имеют три возможных курса действий:

    • Не выполнять никаких действий. Событие остается необработанным и переходит к следующему прослушивателю.

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

    • Выполнить кода в ответ на событие. Пометить событие как обработанное в данных события, передаваемых обработчику, потому что предпринятое действие считается достаточно существенным, чтобы пометить событие как обработанное. Событие все еще перенаправляется к следующему прослушивателю, но с Handled = true его данными событий, поэтому только handledEventsToo прослушиватели имеют возможность вызывать дополнительные обработчики.

Этот концептуальный проект определяется описанным выше поведением маршрутизации: более сложная (хотя и по-прежнему возможными в коде или стилях) присоединять обработчики для перенаправленных событий, которые вызываются, даже если предыдущий обработчик маршрута уже имеет значение Handled true .

Дополнительные сведения об Handled обработке классов перенаправленных событий и рекомендации о том, когда нужно пометить перенаправленное событие как Handled , см. в разделе Маркировка перенаправленных событий как обработанных и обработка классов.

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

Обработчики классов

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

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

Вложенные события в WPF

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

Система ввода WPF широко использует вложенные события. Однако практически все эти вложенные события перенаправляются через базовые элементы. События ввода затем отображаются как эквивалент невложенных перенаправленных событий, которые являются членами класса базового элемента. Например, базовое присоединенное событие Mouse.MouseDown может быть проще обработано в любом заданном с UIElement помощью MouseDown , вместо того чтобы обрабатываться UIElement с помощью синтаксиса вложенных событий в XAML коде или.

Дополнительные сведения о вложенных событиях в WPF см. в разделе Общие сведения о вложенных событиях.

Полные имена событий в XAML

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

<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
  <StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
    <Button Name="YesButton" Width="Auto" >Yes</Button>
    <Button Name="NoButton" Width="Auto" >No</Button>
    <Button Name="CancelButton" Width="Auto" >Cancel</Button>
  </StackPanel>
</Border>

Здесь прослушиватель родительского элемента, в который добавляется обработчик, — это StackPanel . Однако он добавляет обработчик для перенаправленного события, которое было объявлено и будет вызываться Button классом ( ButtonBase фактически, но доступно Button через наследование). Button "владеет" событием, но система перенаправленных событий позволяет обработчикам любого перенаправленного события прикрепляться к UIElement любому ContentElement прослушивателю экземпляра, который в противном случае может присоединить прослушиватели для события среды CLR. Пространством имен xmlns по умолчанию для этих полных имен атрибутов событий обычно является пространство имен xmlns WPF по умолчанию, но можно также указать префиксные пространства имен для пользовательских перенаправленных событий. Дополнительные сведения о xmlns см. в разделе Пространства имен XAML и сопоставление пространств имен для WPF XAML.

События ввода WPF

Перенаправленные события в платформе WPF часто применяются для событий ввода. В WPF имена перенаправляемых по нисходящей событий по соглашению указываются с префиксом Preview. События ввода часто возникают попарно, одно маршрутизируется по восходящей, другое — по нисходящей. Например, KeyDown событие и PreviewKeyDown событие имеют одинаковую сигнатуру, первый из которых является восходящей событием ввода, а второй — событием ввода с нисходящей маршрутизацией. Иногда события ввода имеют только восходящую или, возможно, только прямую маршрутизацию. В документации в разделах, посвященных перенаправленным событиям, содержатся перекрестные ссылки на аналогичные перенаправленные события с альтернативной стратегией маршрутизации, если такие перенаправленные события существуют, и ссылки на разделы документации для уточнения стратегии маршрутизации каждого перенаправленного события.

События ввода WPF, возникающие попарно, реализуются таким образом, что одно действие пользователя из входных данных, такое как нажатие кнопки мыши, последовательно вызовет оба перенаправленные события пары. Сначала вызывается событие, которое маршрутизируется по нисходящей. Затем вызывается событие, которое маршрутизируется по восходящей. Эти два события буквально используют один и тот же экземпляр данных события, так как RaiseEvent вызов метода в реализующем классе, вызывающем событие восходящей маршрутизации, прослушивает данные события из события туннелирования и повторно использует их в новом вызванном событии. Прослушиватели с обработчиками для события, маршрутизирующегося по нисходящей, могут первыми пометить перенаправленное событие как обработанное (сначала обработчики классов, затем обработчики экземпляров). Если элемент при нисходящей маршрутизации отметил перенаправленное событие как обработанное, уже обработанные данные события отправляются для события, маршрутизирующегося по восходящей, и типичные обработчики, вложенные для эквивалентных маршрутизируемых по восходящей событий, не будут вызываться. Внешне это будет выглядеть так, как будто обработанное маршрутизируемое по восходящей событие и не вызывалось. Поведение при обработке полезно использовать при композиции элементов управления, где требуется, чтобы конечный элемент управления (а не его составные части) создавал отчеты о событиях проверки нажатия при вводе или событиях ввода на основе фокуса. Конечный элемент управления находится ближе к корневому элементу в композиции и поэтому имеет возможность обработать событие, передаваемое по нисходящей, первым и, возможно, "заменить" это перенаправленное событие более подходящим для данного элемента управления как часть кода, которая резервирует класс элемента управления.

В качестве иллюстрации того, как обрабатываются события ввода, рассмотрим следующий пример. На следующем рисунке дерева leaf element #2 является источником PreviewMouseDown события а, а затем — MouseDown .

Схема маршрутизации события

Порядок обработки событий выглядит следующим образом.

  1. PreviewMouseDown (нисходящее) на корневом элементе.

  2. PreviewMouseDown (нисходящее) на промежуточном элементе № 1.

  3. PreviewMouseDown (нисходящее) на исходном элементе № 2.

  4. MouseDown (всплывающее) на исходном элементе № 2.

  5. MouseDown (всплывающее) на промежуточном элементе № 1.

  6. MouseDown (всплывающее) на корневом элементе.

Делегат обработчика перенаправленных событий содержит ссылки на два объекта: объект, который вызвал событие, и объект, в котором был вызван обработчик. Объект, в котором был вызван обработчик, указывается с помощью параметра sender. Объект, в котором событие было впервые вызвано Source свойством в данных события. Перенаправленное событие по-прежнему может создаваться и обрабатываться одним и тем же объектом sender . в этом случае и Source идентичны (это происходит в шагах 3 и 4 в списке "пример обработки событий").

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

Как правило, после пометки события ввода Handled дополнительные обработчики не вызываются. Обычно события ввода помечаются как обработанные сразу же после вызова обработчика, который при обработке события ввода опирается на логику приложения.

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

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

В определенных классах обработка классов используется для определенных событий ввода, как правило, с целью переопределения значения событий ввода, вызываемых пользователем, и вызова новых событий. Дополнительные сведения см. в разделе Маркировка перенаправленных событий как обработанных и обработка классов.

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

EventSetters и EventTriggers

В стилях можно включить некоторые предварительно объявленные XAML синтаксисы обработки событий в разметку с помощью EventSetter . При применении стиля указанный обработчик добавляется в экземпляр стиля. Можно объявить EventSetter только для перенаправленного события. Пример. Обратите внимание, что указанный метод b1SetColor находится в файле кода программной части.

<StackPanel
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  x:Class="SDKSample.EventOvw2"
  Name="dpanel2"
  Initialized="PrimeHandledToo"
>
  <StackPanel.Resources>
    <Style TargetType="{x:Type Button}">
      <EventSetter Event="Click" Handler="b1SetColor"/>
    </Style>
  </StackPanel.Resources>
  <Button>Click me</Button>
  <Button Name="ThisButton" Click="HandleThis">
    Raise event, handle it, use handled=true handler to get it anyway.
  </Button>
</StackPanel>

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

Другим специализированным синтаксисом, объединяющим функции перенаправленного события и анимации, WPF является EventTrigger . Как и в EventSetter случае, для можно использовать только перенаправленные события EventTrigger . Как правило, выражение EventTrigger объявляется как часть стиля, но EventTrigger также может быть объявлено в элементах уровня страницы как часть Triggers коллекции или в ControlTemplate . EventTriggerПозволяет указать Storyboard , который запускается каждый раз, когда перенаправленное событие достигает элемента в его маршруте, который объявляет EventTrigger для этого события. Преимущество EventTrigger обработки события и его запуск существующей раскадровки заключается в том, что EventTrigger обеспечивает лучший контроль над раскадровкой и ее поведение во время выполнения. Дополнительные сведения см. в разделе Использование триггеров событий для управления раскадровкой после ее запуска.

Дополнительные сведения о перенаправленных событиях

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

См. также раздел