Поделиться через


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

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

В этом разделе содержатся следующие подразделы.

  • Предварительные требования
  • Что такое перенаправленное событие?
  • Стратегии маршрутизации
  • Зачем использовать перенаправленные события?
  • Добавление и реализация обработчика событий для перенаправленного события
  • Обработчики классов
  • Вложенные события в WPF
  • Полные имена событий в языке XAML
  • События ввода WPF
  • EventSetters и EventTriggers
  • Дополнительные сведения о перенаправленных событиях
  • Связанные разделы

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

В этом разделе предоставляется набор базовых знаний о common language runtime (CLR) и объектно-ориентированном программировании, а также понятие представления связей между элементами WPF в виде дерева. Чтобы понять примеры в этом разделе, следует также понимать Extensible Application Markup Language (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 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
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;
}

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

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

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

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

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

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
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); }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
        'logic to handle the Click event


...


      End Sub
void b1SetColor(object sender, RoutedEventArgs args)
{
  //logic to handle the Click event


...


}

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

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

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

       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
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     
 }

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

        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
void MakeButton2()
{
  Button b2 = new Button();
  b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
  //logic to handle the Click event     
}

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

При использовании 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, который в противном случае может присоединить прослушиватели для события common language runtime (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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://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. Дополнительные сведения о пользовательских событиях см. в разделе Практическое руководство. Создание пользовательских перенаправленных событий.

См. также

Ссылки

EventManager

RoutedEvent

RoutedEventArgs

Основные понятия

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

Общие сведения о входных данных

Общие сведения о системе команд

Пользовательские свойства зависимостей

Деревья в WPF

Шаблоны слабых событий