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

В этом разделе описывается понятие перенаправленных событий в Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF).This topic describes the concept of routed events in Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF). Здесь определяется терминология перенаправленных событий, описывается, как перенаправленные события маршрутизируются через дерево элементов, кратко описываются способы обработки перенаправленных событий, а также способы создания пользовательских перенаправленных событий.The topic defines routed events terminology, describes how routed events are routed through a tree of elements, summarizes how you handle routed events, and introduces how to create your own custom routed events.

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

В этом разделе предполагается, что у вас есть базовые знания о среде CLR и объектно-ориентированном программировании, а также понятие о том, как отношения WPFWPF между элементами можно представить в виде дерева.This topic assumes that you have basic knowledge of the common language runtime (CLR) and object-oriented programming, as well as the concept of how the relationships between WPFWPF elements can be conceptualized as a tree. Чтобы выполнить примеры в этом разделе, следует также понимать Язык XAMLExtensible Application Markup Language (XAML) и знать, как писать простые приложения или страницы WPFWPF.In order to follow the examples in this topic, you should also understand Язык XAMLExtensible Application Markup Language (XAML) and know how to write very basic WPFWPF applications or pages. Дополнительные сведения см. в разделе Пошаговое руководство: Мое первое классическое приложение WPF и Общие сведения о XAML (WPF).For more information, see Walkthrough: My first WPF desktop application and XAML Overview (WPF).

Что такое перенаправленное событие?What Is a Routed Event?

Перенаправленные события можно рассматривать с точки зрения функциональности или реализации.You can think about routed events either from a functional or implementation perspective. Здесь приводятся оба определения, чтобы пользователи могли выбрать наиболее подходящее для себя.Both definitions are presented here, because some people find one or the other definition more useful.

Функциональное определение: Перенаправленное событие — это тип события, которое может вызывать обработчики для нескольких прослушивателей в дереве элементов, а не только для объекта, вызвавшего событие.Functional definition: A routed event is a type of event that can invoke handlers on multiple listeners in an element tree, rather than just on the object that raised the event.

Определение реализации: Перенаправленное событие — это событие CLR, которое поддерживается экземпляром RoutedEvent класса и обрабатывается Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) системой событий.Implementation definition: A routed event is a CLR event that is backed by an instance of the RoutedEvent class and is processed by the Windows Presentation Foundation (WPF)Windows Presentation Foundation (WPF) event system.

Обычно в приложении WPFWPF содержится много элементов.A typical WPFWPF application contains many elements. В зависимости от того, созданы элементы в коде или объявлены в XAMLXAML, они связаны в дереве элементов друг с другом.Whether created in code or declared in XAMLXAML, these elements exist in an element tree relationship to each other. Маршрут события может проходить в одном из двух направлений в зависимости от определения события, но обычно маршрут проходит от исходного элемента и затем "всплывает" вверх по дереву элементов до тех пор, пока не достигнет корневого элемента дерева (как правило, страница или окно).The event route can travel in one of two directions depending on the event definition, but generally the route travels from the source element and then "bubbles" upward through the element tree until it reaches the element tree root (typically a page or a window). Концепция всплывания может быть знакома, если имеется опыт работы с моделью DHTML-объектов.This bubbling concept might be familiar to you if you have worked with the DHTML object model previously.

Рассмотрим следующее простое дерево элементов.Consider the following simple element tree:

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

Это дерево элементов выглядит примерно следующим образом.This element tree produces something like the following:

Кнопки "Да", "Нет" и "Отмена"Yes, No, and Cancel buttons

В этом упрощенном дереве элементов источником Click события является один Button из элементов, а в зависимости от того, Button какой элемент был нажат, первым элементом, который имеет возможность справиться с событием.In this simplified element tree, the source of a Click event is one of the Button elements, and whichever Button was clicked is the first element that has the opportunity to handle the event. Но если к событию не прикрепляется Button обработчик, то событие будет перенаправлено вверх Button к родительскому элементу в StackPanelдереве элементов, то есть.But if no handler attached to the Button acts on the event, then the event will bubble upwards to the Button parent in the element tree, which is the StackPanel. Потенциально событие переносится в Border, а затем выходит в корень страницы дерева элементов (не показано).Potentially, the event bubbles to Border, and then beyond to the page root of the element tree (not shown).

Иными словами, маршрут события для этого Click события:In other words, the event route for this Click event is:

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

Сценарии верхнего уровня для перенаправленных событийTop-level Scenarios for Routed Events

Ниже приведен краткий обзор сценариев, которые послужили концепцией перенаправленных событий, и почему стандартное событие CLR не было достаточным для этих сценариев:The following is a brief summary of the scenarios that motivated the routed event concept, and why a typical CLR event was not adequate for these scenarios:

Композиция элементов управления и инкапсуляция: Различные элементы управления WPFWPF в имеют обширную модель содержимого.Control composition and encapsulation: Various controls in WPFWPF have a rich content model. Например, можно поместить изображение в Button, которое эффективно расширяет визуальное дерево кнопки.For example, you can place an image inside of a Button, which effectively extends the visual tree of the button. Однако добавленное изображение не должно прерывать проверку попадания, которая вызывает реакцию Click кнопки на ее содержимое, даже если пользователь щелкает Пиксели, которые технически являются частью изображения.However, the added image must not break the hit-testing behavior that causes a button to respond to a Click of its content, even if the user clicks on pixels that are technically part of the image.

Точки вложенности единственного обработчика: В Windows FormsWindows Formsдля обработки событий, которые могут быть вызваны из нескольких элементов, необходимо несколько раз присоединить один и тот же обработчик.Singular handler attachment points: In Windows FormsWindows Forms, you would have to attach the same handler multiple times to process events that could be raised from multiple elements. Перенаправленные события позволяют присоединить обработчик только один раз, как было показано в предыдущем примере, и при необходимости использовать логику обработки для определения места возникновения события.Routed events enable you to attach that handler only once, as was shown in the previous example, and use handler logic to determine where the event came from if necessary. Например, это может быть обработчиком для ранее показанного XAMLXAML.For instance, this might be the handler for the previously shown XAMLXAML:

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

Обработка класса: Перенаправленные события разрешает статический обработчик, определяемый классом.Class handling: Routed events permit a static handler that is defined by the class. Этот обработчик классов может обрабатывать событие раньше любого вложенного обработчика экземпляров.This class handler has the opportunity to handle an event before any attached instance handlers can.

Ссылка на событие без отражения: Для определенных методов кода и разметки требуется способ обнаружения определенного события.Referencing an event without reflection: Certain code and markup techniques require a way to identify a specific event. Перенаправленное событие создает RoutedEvent поле в виде идентификатора, которое предоставляет надежный метод идентификации событий, не требующий статического отражения или отражение во время выполнения.A routed event creates a RoutedEvent field as an identifier, which provides a robust event identification technique that does not require static or run-time reflection.

Реализация перенаправленных событийHow Routed Events Are Implemented

Перенаправленное событие — это событие CLR, которое поддерживается экземпляром RoutedEvent класса и регистрируется WPFWPF в системе событий.A routed event is a CLR event that is backed by an instance of the RoutedEvent class and registered with the WPFWPF event system. Экземпляр, полученный из регистрации, обычно static сохраняется readonly public как элемент поля класса, который регистрирует и, таким образом, "владеет" перенаправленным событием. RoutedEventThe RoutedEvent instance obtained from registration is typically retained as a public static readonly field member of the class that registers and thus "owns" the routed event. Соединение с событием CLR с одинаковым именем (которое иногда называется "оболочкой") достигается путем переопределения add реализаций и remove для события CLR.The connection to the identically named CLR event (which is sometimes termed the "wrapper" event) is accomplished by overriding the add and remove implementations for the CLR event. Как правило, add и remove остаются неявными по умолчанию и используют соответствующий синтаксис события определенного языка для добавления и удаления обработчиков события.Ordinarily, the add and remove are left as an implicit default that uses the appropriate language-specific event syntax for adding and removing handlers of that event. Механизм резервного копирования и подключения перенаправленных событий концептуально похож на то, как свойство зависимостей является свойством CLR, которое поддерживается DependencyProperty классом и регистрируется WPFWPF в системе свойств.The routed event backing and connection mechanism is conceptually similar to how a dependency property is a CLR property that is backed by the DependencyProperty class and registered with the WPFWPF property system.

В следующем примере показано объявление пользовательского Tap перенаправленного события, включая регистрацию и раскрытие RoutedEvent поля remove идентификатора, а также add реализаций для Tap события CLR.The following example shows the declaration for a custom Tap routed event, including the registration and exposure of the RoutedEvent identifier field and the add and remove implementations for the Tap CLR 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); }
}
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

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

Чтобы добавить обработчик для события с помощью XAMLXAML, следует объявить имя события как атрибут для элемента, который является прослушивателем событий.To add a handler for an event using XAMLXAML, you declare the event name as an attribute on the element that is an event listener. Значением атрибута является имя метода реализуемого обработчика, который должен существовать в разделяемом классе файла кода программной части.The value of the attribute is the name of your implemented handler method, which must exist in the partial class of the code-behind file.

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

XAMLXAML Синтаксис для добавления стандартных обработчиков событий среды CLR аналогичен для добавления обработчиков перенаправленных событий, так как вы действительно добавляете обработчики в оболочку событий CLR, которая имеет реализацию перенаправленного события.The XAMLXAML syntax for adding standard CLR event handlers is the same for adding routed event handlers, because you are really adding handlers to the CLR event wrapper, which has a routed event implementation underneath. Дополнительные сведения о добавлении обработчиков событий в XAMLXAML см. в разделе Общие сведения о языке XAML (WPF).For more information about adding event handlers in XAMLXAML, see XAML Overview (WPF).

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

Перенаправленные события используют одну из трех стратегий маршрутизации.Routed events use one of three routing strategies:

  • Восходящей маршрутизации Вызываются обработчики событий в источнике события.Bubbling: Event handlers on the event source are invoked. Перенаправленное событие затем следует к родительским элементам до достижения корневого элемента дерева.The routed event then routes to successive parent elements until reaching the element tree root. Большинство перенаправленных событий используют стратегию восходящей маршрутизации.Most routed events use the bubbling routing strategy. События восходящей маршрутизации обычно используются для получения отчета об изменении входных данных или состояния от различных элементов управления или других элементов пользовательского интерфейса.Bubbling routed events are generally used to report input or state changes from distinct controls or other UI elements.

  • Направлений Только сам исходный элемент получает возможность вызывать обработчики в ответе.Direct: Only the source element itself is given the opportunity to invoke handlers in response. Это является аналогом "маршрутизации", которая используется Windows FormsWindows Forms для события.This is analogous to the "routing" that Windows FormsWindows Forms uses for events. Однако, в отличие от стандартного события CLR, прямые перенаправленные события поддерживают обработку классов (обработка классов объясняется в следующем разделе) и может использоваться EventSetter и. EventTriggerHowever, unlike a standard CLR event, direct routed events support class handling (class handling is explained in an upcoming section) and can be used by EventSetter and EventTrigger.

  • Туннелирование Изначально вызываются обработчики событий в корне дерева элементов.Tunneling: Initially, event handlers at the element tree root are invoked. Перенаправленное событие затем передается по маршруту через последовательные дочерние элементы к узловому элементу, который является источником перенаправленного события (элементом, вызвавшим перенаправленное событие).The routed event then travels a route through successive child elements along the route, towards the node element that is the routed event source (the element that raised the routed event). Нисходящая маршрутизация событий часто используется или обрабатывается как часть композиции для элемента управления таким образом, что события из составных частей композиции могут намеренно подавляться или заменяться событиями, которые определены для полного контроля.Tunneling routed events are often used or handled as part of the compositing for a control, such that events from composite parts can be deliberately suppressed or replaced by events that are specific to the complete control. Входные события, которые содержатся в WPFWPF, часто реализуются в виде пары нисходящей и восходящей маршрутизации.Input events provided in WPFWPF often come implemented as a tunneling/bubbling pair. События нисходящей маршрутизации также иногда называют событиями предварительного просмотра по причине используемого для пар соглашения об именах.Tunneling events are also sometimes referred to as Preview events, because of a naming convention that is used for the pairs.

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

Разработчикам приложений не всегда необходимо знать, реализуется ли обрабатываемое событие как перенаправленное.As an application developer, you do not always need to know or care that the event you are handling is implemented as a routed event. Перенаправленные события имеют особое поведение, но такое событие остается невидимым, если обрабатывается в элементе, где оно возникает.Routed events have special behavior, but that behavior is largely invisible if you are handling an event on the element where it is raised.

Перенаправленные события являются мощным инструментом при использовании их в одном из предлагаемых сценариев: при определении общих обработчиков для общего корня, при композиции собственного элемента управления или при определении собственного класса пользовательского элемента управления.Where routed events become powerful is if you use any of the suggested scenarios: defining common handlers at a common root, compositing your own control, or defining your own custom control class.

Прослушиватели перенаправленных событий и источники перенаправленных событий не требуются для совместного использования общего события в их иерархии.Routed event listeners and routed event sources do not need to share a common event in their hierarchy. Any UIElement илиContentElement может быть прослушивателем событий для любого перенаправленного события.Any UIElement or ContentElement can be an event listener for any routed event. Таким образом, можно использовать полный набор перенаправленных событий, доступных во всем рабочем наборе API в качестве концептуального "интерфейса", благодаря которому разнородные элементы в приложении могут обмениваться данными о событиях.Therefore, you can use the full set of routed events available throughout the working API set as a conceptual "interface" whereby disparate elements in the application can exchange event information. Эта концепция "интерфейса" для перенаправленных событий особенно применима для событий ввода.This "interface" concept for routed events is particularly applicable for input events.

Перенаправленные события могут также использоваться для связи элементов дерева, так как данные событий сохраняются для каждого элемента в маршруте.Routed events can also be used to communicate through the element tree, because the event data for the event is perpetuated to each element in the route. Один элемент может изменить что-либо в данных события, и это изменение будет доступно для следующего элемента в маршруте.One element could change something in the event data, and that change would be available to the next element in the route.

В отличие от маршрутизации, существует еще две причины, по которым любое заданное WPFWPF событие может быть реализовано в виде перенаправленного события вместо стандартного события CLR.Other than the routing aspect, there are two other reasons that any given WPFWPF event might be implemented as a routed event instead of a standard CLR event. При реализации собственных событий рекомендуется учитывать приведенные далее принципы.If you are implementing your own events, you might also consider these principles:

  • Некоторые WPFWPF функции стилизации и создания шаблонов EventSetter , EventTrigger такие как и, занимают событие, на которое указывает ссылка, как перенаправленное событие.Certain WPFWPF styling and templating features such as EventSetter and EventTrigger require the referenced event to be a routed event. Это сценарий идентификатора события, упомянутого выше.This is the event identifier scenario mentioned earlier.

  • Перенаправленные события поддерживают механизм обработки классов, в силу чего класс может указывать статические методы, имеющие возможность обрабатывать перенаправленные события до того, как любой зарегистрированный обработчик экземпляров сможет получить к ним доступ.Routed events support a class handling mechanism whereby the class can specify static methods that have the opportunity to handle routed events before any registered instance handlers can access them. Это очень полезно при разработке элемента управления, поскольку класс может управляться с помощью событий, что не может быть случайно подавлено обработкой события в экземпляре.This is very useful in control design, because your class can enforce event-driven class behaviors that cannot be accidentally suppressed by handling an event on an instance.

Каждое из описанных выше рассуждений рассматривается в отдельной части этого раздела.Each of the above considerations is discussed in a separate section of this topic.

Добавление и реализация обработчика событий для перенаправленного событияAdding and Implementing an Event Handler for a Routed Event

Чтобы добавить обработчик событий в XAMLXAML, просто добавьте имя события в элемент как атрибут и установите значение атрибута в качестве имени обработчика событий, который реализует соответствующий делегат, как показано в следующем примере.To add an event handler in XAMLXAML, you simply add the event name to an element as an attribute and set the attribute value as the name of the event handler that implements an appropriate delegate, as in the following example.

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

b1SetColorимя реализованного обработчика, содержащего код, обрабатывающий Click событие.b1SetColor is the name of the implemented handler that contains the code that handles the Click event. b1SetColorдолжен иметь такую же сигнатуру, RoutedEventHandler что и делегат, который является делегатом обработчика Click событий для события.b1SetColor must have the same signature as the RoutedEventHandler delegate, which is the event handler delegate for the Click event. Первый параметр всех делегатов обработчиков перенаправленных событий указывает элемент, к которому добавляется обработчик событий, а второй параметр указывает данные для события.The first parameter of all routed event handler delegates specifies the element to which the event handler is added, and the second parameter specifies the data for the event.

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является базовым делегатом обработчика перенаправленных событий.RoutedEventHandler is the basic routed event handler delegate. Для перенаправленных событий, которые являются специализированными для определенных элементов управления или скриптов, делегаты, которые используются для обработчиков перенаправленных событий, также могут быть более специализированными, чтобы они могли передавать определенные данные события.For routed events that are specialized for certain controls or scenarios, the delegates to use for the routed event handlers also might become more specialized, so that they can transmit specialized event data. Например, в общем сценарии ввода может быть обработано DragEnter перенаправленное событие.For instance, in a common input scenario, you might handle a DragEnter routed event. Обработчик должен реализовать DragEventHandler делегат.Your handler should implement the DragEventHandler delegate. Используя наиболее конкретный делегат, можно обработать DragEventArgs в обработчике и Data прочитать свойство, которое содержит полезные данные буфера обмена операции перетаскивания.By using the most specific delegate, you can process the DragEventArgs in the handler and read the Data property, which contains the clipboard payload of the drag operation.

Полный пример добавления обработчика событий к элементу с помощью XAMLXAML см. в разделе Обработка перенаправленных событий.For a complete example of how to add an event handler to an element using XAMLXAML, see Handle a Routed Event.

Добавить обработчик для перенаправленного события в приложении, созданном в коде, достаточно просто.Adding a handler for a routed event in an application that is created in code is straightforward. Обработчики перенаправленных событий всегда можно добавлять с помощью вспомогательного метода AddHandler (который является тем же методом, для addкоторого существуют резервные вызовы.) Однако существующие перенаправленные события WPFWPF обычно имеют резервную реализацию add и логику remove, позволяющую добавлять обработчики перенаправленных событий с помощью синтаксиса событий для определенного языка, являющегося более понятным, чем вспомогательный метод.Routed event handlers can always be added through a helper method AddHandler (which is the same method that the existing backing calls for add.) However, existing WPFWPF routed events generally have backing implementations of add and remove logic that allow the handlers for routed events to be added by a language-specific event syntax, which is more intuitive syntax than the helper method. Ниже приведен пример использования вспомогательного метода.The following is an example usage of the helper method:

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 несколько отличается синтаксисом оператора из-за его обработки разыменованием):The next example shows the C# operator syntax (Visual Basic has slightly different operator syntax because of its handling of dereferencing):

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

Пример добавления обработчика событий в коде см. в разделе Добавление обработчика событий с помощью кода.For an example of how to add an event handler in code, see Add an Event Handler Using Code.

Если вы используете Visual Basic, можно также использовать Handles ключевое слово для добавления обработчиков в составе объявлений обработчика.If you are using Visual Basic, you can also use the Handles keyword to add handlers as part of the handler declarations. Дополнительные сведения см. в разделе Обработка событий в Visual Basic и WPF.For more information, see Visual Basic and WPF Event Handling.

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

Все перенаправленные события совместно используют общий базовый класс данных событий RoutedEventArgs,.All routed events share a common event data base class, RoutedEventArgs. RoutedEventArgsHandled определяет свойство, принимающее логическое значение.RoutedEventArgs defines the Handled property, which takes a Boolean value. Свойство предназначено для включения любого обработчика событий на маршруте, чтобы пометить перенаправленное событие как обработанное, Handled задав для trueпараметра значение. HandledThe purpose of the Handled property is to enable any event handler along the route to mark the routed event as handled, by setting the value of Handled to true. После обработки обработчиком в одном элементе в маршруте совместно используемые данные события снова предоставляются каждому прослушивателю в маршруте.After being processed by the handler at one element along the route, the shared event data is again reported to each listener along the route.

Значение Handled влияет на то, как перенаправленное событие сообщается или обрабатывается, когда оно перемещается дальше вдоль маршрута.The value of Handled affects how a routed event is reported or processed as it travels further along the route. Если Handled параметр true находится в данных события для перенаправленного события, то обработчики, которые прослушивают перенаправленное событие для других элементов, обычно больше не вызываются для этого конкретного экземпляра события.If Handled is true in the event data for a routed event, then handlers that listen for that routed event on other elements are generally no longer invoked for that particular event instance. Это справедливо как для обработчиков, присоединенных к XAMLXAML, так и для обработчиков, добавленных с помощью синтаксиса присоединения обработчика событий конкретного языка, например += или Handles.This is true both for handlers attached in XAMLXAML and for handlers added by language-specific event handler attachment syntaxes such as += or Handles. Для наиболее распространенных сценариев обработчика Пометка события как обработанного путем Handled установки true параметра в значение будет "прекращать" маршрутизацию для маршрута туннелирования или для восходящей маршрутизации, а также для любого события, которое обрабатывается в точке в маршруте обработчиком класса.For most common handler scenarios, marking an event as handled by setting Handled to true will "stop" routing for either a tunneling route or a bubbling route, and also for any event that is handled at a point in the route by a class handler.

Однако существует механизм "handledEventsToo", при котором прослушиватели по-прежнему могут запускать обработчики в ответ на перенаправляемые события, где Handled находится true в данных события.However, there is a "handledEventsToo" mechanism whereby listeners can still run handlers in response to routed events where Handled is true in the event data. Другими словами, маршрут события в действительности не останавливается при пометке события в данных события как обработанного.In other words, the event route is not truly stopped by marking the event data as handled. Механизм handledEventsToo можно использовать только в коде или в EventSetter:You can only use the handledEventsToo mechanism in code, or in an EventSetter:

  • В коде вместо использования синтаксиса событий, зависящего от языка, который работает для общих событий CLR, WPFWPF вызовите AddHandler(RoutedEvent, Delegate, Boolean) метод, чтобы добавить обработчик.In code, instead of using a language-specific event syntax that works for general CLR events, call the WPFWPF method AddHandler(RoutedEvent, Delegate, Boolean) to add your handler. Задайте для handledEventsToo значение true.Specify the value of handledEventsToo as true.

  • В присвойте атрибуту trueзначение. HandledEventsToo EventSetterIn an EventSetter, set the HandledEventsToo attribute to be true.

В дополнение к поведению, Handled которое получается в перенаправленных событиях, Handled концепция имеет влияние на то, как следует проектировать приложение и написать код обработчика событий.In addition to the behavior that Handled state produces in routed events, the concept of Handled has implications for how you should design your application and write the event handler code. Можно концептуализировать Handled как простой протокол, предоставляемый перенаправленными событиями.You can conceptualize Handled as being a simple protocol that is exposed by routed events. Именно вы используете этот протокол, но концептуальное проектирование того, как предполагается использовать значение Handled , выглядит следующим образом:Exactly how you use this protocol is up to you, but the conceptual design for how the value of Handled is intended to be used is as follows:

  • Если перенаправленное событие помечено как обработанное, то затем его не требуется снова обрабатывать другими элементами в маршруте.If a routed event is marked as handled, then it does not need to be handled again by other elements along that route.

  • Если перенаправленное событие не помечено как обработанное, то другие прослушиватели, ранее находились в маршруте, отказались от регистрации обработчика, или обработчики, которые были зарегистрированы, отказались trueот обработки данных события и присвоить параметру значение Handled .If a routed event is not marked as handled, then other listeners that were earlier along the route have chosen either not to register a handler, or the handlers that were registered chose not to manipulate the event data and set Handled to true. (Или, возможно, текущий прослушиватель является первой точкой в маршруте.) Обработчики на текущем прослушивателе имеют три возможных варианта действий.(Or, it is of course possible that the current listener is the first point in the route.) Handlers on the current listener now have three possible courses of action:

    • Не выполнять никаких действий. Событие остается необработанным и переходит к следующему прослушивателю.Take no action at all; the event remains unhandled, and the event routes to the next listener.

    • Выполнить код в ответ на событие, при этом убедиться, что выполненное действие не было достаточно существенным, чтобы пометить событие как обработанное.Execute code in response to the event, but make the determination that the action taken was not substantial enough to warrant marking the event as handled. Событие перенаправляется к следующему прослушивателю.The event routes to the next listener.

    • Выполнить кода в ответ на событие.Execute code in response to the event. Пометить событие как обработанное в данных события, передаваемых обработчику, потому что предпринятое действие считается достаточно существенным, чтобы пометить событие как обработанное.Mark the event as handled in the event data passed to the handler, because the action taken was deemed substantial enough to warrant marking as handled. Событие все еще перенаправляется к следующему прослушивателю, Handled но с = true его данными событий, поэтому handledEventsToo только прослушиватели имеют возможность вызывать дополнительные обработчики.The event still routes to the next listener, but with Handled=true in its event data, so only handledEventsToo listeners have the opportunity to invoke further handlers.

Этот концептуальный проект усиливается описанным выше поведением маршрутизации: более сложная (хотя и возможно в коде или стилях) присоединение обработчиков для перенаправленных событий, которые вызываются, даже если предыдущий обработчик маршрута уже задан Handled. вtrue.This conceptual design is reinforced by the routing behavior mentioned earlier: it is more difficult (although still possible in code or styles) to attach handlers for routed events that are invoked even if a previous handler along the route has already set Handled to true.

Дополнительные сведения об Handledобработке классов перенаправленных событий и рекомендации о том, когда нужно пометить перенаправленное событие как Handled, см. в разделе Маркировка перенаправленных событий как обработанных и обработка классов.For more information about Handled, class handling of routed events, and recommendations about when it is appropriate to mark a routed event as Handled, see Marking Routed Events as Handled, and Class Handling.

В приложениях весьма распространена обработка только перенаправляемого по восходящей события в объекте, который вызывал его, независимо от характеристик маршрутизации события.In applications, it is quite common to just handle a bubbling routed event on the object that raised it, and not be concerned with the event's routing characteristics at all. Однако все же рекомендуется помечать перенаправленное событие как обработанное в данных события, чтобы избежать непредвиденных побочных эффектов на случай, если элемент, который далее следует в дереве элементов, имеет вложенный обработчик для этого же перенаправленного события.However, it is still a good practice to mark the routed event as handled in the event data, to prevent unanticipated side effects just in case an element that is further up the element tree also has a handler attached for that same routed event.

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

При определении класса, который является каким-то образом производным от DependencyObject, можно также определить и присоединить обработчик класса для перенаправленного события, которое является объявленным или унаследованным членом события класса.If you are defining a class that derives in some way from DependencyObject, you can also define and attach a class handler for a routed event that is a declared or inherited event member of your class. Обработчики классов вызываются ранее любого обработчика прослушивателей экземпляров, присоединенного к экземпляру этого класса, всякий раз, когда перенаправленное событие встречает экземпляр элемента в своем маршруте.Class handlers are invoked before any instance listener handlers that are attached to an instance of that class, whenever a routed event reaches an element instance in its route.

Некоторые элементы управления WPFWPF имеют внутреннюю обработку классов для некоторых перенаправленных событий.Some WPFWPF controls have inherent class handling for certain routed events. Может показаться, что перенаправленное событие не возникает никогда, но на самом деле оно обрабатывается классом и перенаправленное событие по-прежнему может потенциально обрабатываться с помощью обработчиков экземпляров при использовании определенных методов.This might give the outward appearance that the routed event is not ever raised, but in reality it is being class handled, and the routed event can potentially still be handled by your instance handlers if you use certain techniques. Кроме того, многие базовые классы и элементы управления предоставляют виртуальные методы, которые могут быть использованы для переопределения поведения при обработке классов.Also, many base classes and controls expose virtual methods that can be used to override class handling behavior. Дополнительные сведения о том, как обрабатывать нежелательные классы и как определить обработку собственного класса в пользовательском классе, см. в разделе Маркировка перенаправленных событий как обработанных и обработка классов.For more information both on how to work around undesired class handling and on defining your own class handling in a custom class, see Marking Routed Events as Handled, and Class Handling.

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

XAMLXAML также определяет специальный тип события, называемого вложенным событием.The XAMLXAML language also defines a special type of event called an attached event. Вложенное событие позволяет добавлять обработчик для конкретного события в произвольный элемент.An attached event enables you to add a handler for a particular event to an arbitrary element. Элементу, обрабатывающему событие, не требуется определять или наследовать вложенное событие, и ни объект, который потенциально может вызвать событие, ни экземпляр обработки места назначения не должны определять или каким-либо иным способом владеть этим событием в качестве элемента класса.The element handling the event need not define or inherit the attached event, and neither the object potentially raising the event nor the destination handling instance must define or otherwise "own" that event as a class member.

Система ввода WPFWPF широко использует вложенные события.The WPFWPF input system uses attached events extensively. Однако практически все эти вложенные события перенаправляются через базовые элементы.However, nearly all of these attached events are forwarded through base elements. События ввода затем отображаются как эквивалент невложенных перенаправленных событий, которые являются членами класса базового элемента.The input events then appear as equivalent non-attached routed events that are members of the base element class. Например, базовое присоединенное событие Mouse.MouseDown может быть проще обработано в любом UIElement заданном MouseDown с помощью UIElement , вместо того чтобы обрабатываться с помощью синтаксиса вложенных событий в XAMLXAML коде или.For instance, the underlying attached event Mouse.MouseDown can more easily be handled on any given UIElement by using MouseDown on that UIElement rather than dealing with attached event syntax either in XAMLXAML or code.

Дополнительные сведения о вложенных событиях в WPFWPF см. в разделе Общие сведения о вложенных событиях.For more information about attached events in WPFWPF, see Attached Events Overview.

Полные имена событий в XAMLQualified Event Names in XAML

Другой синтаксис напоминает синаксис typename. EventName вложенного события, но он не является синтаксисом вложенного события при присоединении обработчиков для перенаправленных событий, вызываемых дочерними элементами.Another syntax usage that resembles typename.eventname attached event syntax but is not strictly speaking an attached event usage is when you attach handlers for routed events that are raised by child elements. Обработчики присоединяются в общем родительском элементе, чтобы воспользоваться преимуществами маршрутизации событий, несмотря на то что общий родительский элемент может не иметь соответствующего перенаправленного события в качестве члена.You attach the handlers to a common parent, to take advantage of event routing, even though the common parent might not have the relevant routed event as a member. Рассмотрим этот пример еще раз.Consider this example again:

<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это.Here, the parent element listener where the handler is added is a StackPanel. Однако он добавляет обработчик для перенаправленного события, которое было объявлено и будет вызываться Button классом (ButtonBase Button фактически, но доступно через наследование).However, it is adding a handler for a routed event that was declared and will be raised by the Button class (ButtonBase actually, but available to Button through inheritance). Button"владеет" событием, но система перенаправленных событий позволяет обработчикам любого перенаправленного события прикрепляться к UIElement любому ContentElement прослушивателю экземпляра, который в противном случае может присоединить прослушиватели для события среды CLR.Button "owns" the event, but the routed event system permits handlers for any routed event to be attached to any UIElement or ContentElement instance listener that could otherwise attach listeners for a common language runtime (CLR) event. Пространством имен xmlns по умолчанию для этих полных имен атрибутов событий обычно является пространство имен xmlns WPFWPF по умолчанию, но можно также указать префиксные пространства имен для пользовательских перенаправленных событий.The default xmlns namespace for these qualified event attribute names is typically the default WPFWPF xmlns namespace, but you can also specify prefixed namespaces for custom routed events. Дополнительные сведения о xmlns см. в разделе Пространства имен XAML и сопоставление пространств имен для WPF XAML.For more information about xmlns, see XAML Namespaces and Namespace Mapping for WPF XAML.

События ввода WPFWPF Input Events

Перенаправленные события в платформе WPFWPF часто применяются для событий ввода.One frequent application of routed events within the WPFWPF platform is for input events. В WPFWPF имена перенаправляемых по нисходящей событий по соглашению указываются с префиксом Preview.In WPFWPF, tunneling routed events names are prefixed with the word "Preview" by convention. События ввода часто возникают попарно, одно маршрутизируется по восходящей, другое — по нисходящей.Input events often come in pairs, with one being the bubbling event and the other being the tunneling event. Например, KeyDown событие PreviewKeyDown и событие имеют одинаковую сигнатуру, первый из которых является восходящей событием ввода, а второй — событием ввода с нисходящей маршрутизацией.For example, the KeyDown event and the PreviewKeyDown event have the same signature, with the former being the bubbling input event and the latter being the tunneling input event. Иногда события ввода имеют только восходящую или, возможно, только прямую маршрутизацию.Occasionally, input events only have a bubbling version, or perhaps only a direct routed version. В документации в разделах, посвященных перенаправленным событиям, содержатся перекрестные ссылки на аналогичные перенаправленные события с альтернативной стратегией маршрутизации, если такие перенаправленные события существуют, и ссылки на разделы документации для уточнения стратегии маршрутизации каждого перенаправленного события.In the documentation, routed event topics cross-reference similar routed events with alternative routing strategies if such routed events exist, and sections in the managed reference pages clarify the routing strategy of each routed event.

События ввода WPFWPF, возникающие попарно, реализуются таким образом, что одно действие пользователя из входных данных, такое как нажатие кнопки мыши, последовательно вызовет оба перенаправленные события пары.WPFWPF input events that come in pairs are implemented so that a single user action from input, such as a mouse button press, will raise both routed events of the pair in sequence. Сначала вызывается событие, которое маршрутизируется по нисходящей.First, the tunneling event is raised and travels its route. Затем вызывается событие, которое маршрутизируется по восходящей.Then the bubbling event is raised and travels its route. Эти два события буквально используют один и тот же экземпляр данных события, RaiseEvent так как вызов метода в реализующем классе, вызывающем событие восходящей маршрутизации, прослушивает данные события из события туннелирования и повторно использует их в новом вызванном событии.The two events literally share the same event data instance, because the RaiseEvent method call in the implementing class that raises the bubbling event listens for the event data from the tunneling event and reuses it in the new raised event. Прослушиватели с обработчиками для события, маршрутизирующегося по нисходящей, могут первыми пометить перенаправленное событие как обработанное (сначала обработчики классов, затем обработчики экземпляров).Listeners with handlers for the tunneling event have the first opportunity to mark the routed event handled (class handlers first, then instance handlers). Если элемент при нисходящей маршрутизации отметил перенаправленное событие как обработанное, уже обработанные данные события отправляются для события, маршрутизирующегося по восходящей, и типичные обработчики, вложенные для эквивалентных маршрутизируемых по восходящей событий, не будут вызываться.If an element along the tunneling route marked the routed event as handled, the already-handled event data is sent on for the bubbling event, and typical handlers attached for the equivalent bubbling input events will not be invoked. Внешне это будет выглядеть так, как будто обработанное маршрутизируемое по восходящей событие и не вызывалось.To outward appearances it will be as if the handled bubbling event has not even been raised. Поведение при обработке полезно использовать при композиции элементов управления, где требуется, чтобы конечный элемент управления (а не его составные части) создавал отчеты о событиях проверки нажатия при вводе или событиях ввода на основе фокуса.This handling behavior is useful for control compositing, where you might want all hit-test based input events or focus-based input events to be reported by your final control, rather than its composite parts. Конечный элемент управления находится ближе к корневому элементу в композиции и поэтому имеет возможность обработать событие, передаваемое по нисходящей, первым и, возможно, "заменить" это перенаправленное событие более подходящим для данного элемента управления как часть кода, которая резервирует класс элемента управления.The final control element is closer to the root in the compositing, and therefore has the opportunity to class handle the tunneling event first and perhaps to "replace" that routed event with a more control-specific event, as part of the code that backs the control class.

В качестве иллюстрации того, как обрабатываются события ввода, рассмотрим следующий пример.As an illustration of how input event processing works, consider the following input event example. На следующем рисунке leaf element #2 дерева является источником PreviewMouseDown MouseDown события а, а затем —.In the following tree illustration, leaf element #2 is the source of both a PreviewMouseDown and then a MouseDown event:

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

Порядок обработки событий выглядит следующим образом.The order of event processing is as follows:

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

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

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

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

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

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

Делегат обработчика перенаправленных событий содержит ссылки на два объекта: объект, который вызвал событие, и объект, в котором был вызван обработчик.A routed event handler delegate provides references to two objects: the object that raised the event and the object where the handler was invoked. Объект, в котором был вызван обработчик, указывается с помощью параметра sender.The object where the handler was invoked is the object reported by the sender parameter. Объект, в котором событие было впервые вызвано Source свойством в данных события.The object where the event was first raised is reported by the Source property in the event data. Перенаправленное событие по-прежнему может создаваться и обрабатываться одним и тем же объектом sender . Source в этом случае и идентичны (это происходит в шагах 3 и 4 в списке "пример обработки событий").A routed event can still be raised and handled by the same object, in which case sender and Source are identical (this is the case with Steps 3 and 4 in the event processing example list).

В Source связи с туннелированием и восходящей маршрутизацией родительские элементы получают входные события, где является одним из их дочерних элементов.Because of tunneling and bubbling, parent elements receive input events where the Source is one of their child elements. Когда важно знать, что представляет собой исходный элемент, можно определить исходный элемент, обратившись к Source свойству.When it is important to know what the source element is, you can identify the source element by accessing the Source property.

Как правило, после пометки Handledсобытия ввода дополнительные обработчики не вызываются.Usually, once the input event is marked Handled, further handlers are not invoked. Обычно события ввода помечаются как обработанные сразу же после вызова обработчика, который при обработке события ввода опирается на логику приложения.Typically, you should mark input events as handled as soon as a handler is invoked that addresses your application-specific logical handling of the meaning of the input event.

Исключением из этой общей инструкции о Handled состоянии является то, что обработчики событий ввода, зарегистрированные для намеренного пропуска Handled состояния данных события, будут по-прежнему вызываться по маршруту.The exception to this general statement about Handled state is that input event handlers that are registered to deliberately ignore Handled state of the event data would still be invoked along either route. Дополнительные сведения см. в разделах События предварительного просмотра или Маркировка перенаправленных событий как обработанных и обработка классов.For more information, see Preview Events or Marking Routed Events as Handled, and Class Handling.

Модель общего использования данных события при нисходящей и восходящей маршрутизации и последовательный вызов сначала нисходящих, а потом всплывающих событий не обязательно выполняются для всех перенаправленных событий.The shared event data model between tunneling and bubbling events, and the sequential raising of first tunneling then bubbling events, is not a concept that is generally true for all routed events. Такое поведение реализуется в зависимости от того, как устройства ввода WPFWPF вызывают или соединяют пары событий ввода.That behavior is specifically implemented by how WPFWPF input devices choose to raise and connect the input event pairs. Дополнительным сценарием является реализация собственных событий ввода, но эту модель также можно реализовывать для собственных событий ввода.Implementing your own input events is an advanced scenario, but you might choose to follow that model for your own input events also.

В определенных классах обработка классов используется для определенных событий ввода, как правило, с целью переопределения значения событий ввода, вызываемых пользователем, и вызова новых событий.Certain classes choose to class-handle certain input events, usually with the intent of redefining what a particular user-driven input event means within that control and raising a new event. Дополнительные сведения см. в разделе Маркировка перенаправленных событий как обработанных и обработка классов.For more information, see Marking Routed Events as Handled, and Class Handling.

Дополнительные сведения о вводе данных и о том, как ввод данных и события взаимодействуют в обычных сценариях приложения, см. в разделе Общие сведения о входных данных.For more information on input and how input and events interact in typical application scenarios, see Input Overview.

EventSetters и EventTriggersEventSetters and EventTriggers

В стилях можно включить некоторые предварительно объявленные XAMLXAML синтаксисы обработки событий в разметку EventSetterс помощью.In styles, you can include some pre-declared XAMLXAML event handling syntax in the markup by using an EventSetter. При применении стиля указанный обработчик добавляется в экземпляр стиля.When the style is applied, the referenced handler is added to the styled instance. Можно объявить EventSetter только для перенаправленного события.You can declare an EventSetter only for a routed event. Пример.The following is an example. Обратите внимание, что указанный метод b1SetColor находится в файле кода программной части.Note that the b1SetColor method referenced here is in a code-behind file.

<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 быть применены к любой кнопке в приложении, и наличие части этого стиля способствует повторному использованию кода даже на уровне разметки.The advantage gained here is that the style is likely to contain a great deal of other information that could apply to any button in your application, and having the EventSetter be part of that style promotes code reuse even at the markup level. Кроме того, EventSetter абстрактные имена методов для обработчиков поочередно изменяются от общего разметки приложения и страницы.Also, an EventSetter abstracts method names for handlers one step further away from the general application and page markup.

Другим специализированным синтаксисом, объединяющим функции WPFWPF перенаправленного события и анимации, EventTriggerявляется.Another specialized syntax that combines the routed event and animation features of WPFWPF is an EventTrigger. Как и EventSetterв случае, EventTriggerдля можно использовать только перенаправленные события.As with EventSetter, only routed events may be used for an EventTrigger. Как правило, EventTrigger выражение объявляется как часть стиля, EventTrigger но также может быть объявлено в Triggers элементах уровня страницы как часть коллекции или в ControlTemplate.Typically, an EventTrigger is declared as part of a style, but an EventTrigger can also be declared on page-level elements as part of the Triggers collection, or in a ControlTemplate. Позволяет указать, который запускается каждый раз, когда перенаправленное событие достигает элемента в его EventTrigger маршруте, который объявляет для этого события. Storyboard EventTriggerAn EventTrigger enables you to specify a Storyboard that runs whenever a routed event reaches an element in its route that declares an EventTrigger for that event. Преимущество EventTrigger обработки события и его запуск существующей раскадровки заключается в EventTrigger том, что обеспечивает лучший контроль над раскадровкой и ее поведение во время выполнения.The advantage of an EventTrigger over just handling the event and causing it to start an existing storyboard is that an EventTrigger provides better control over the storyboard and its run-time behavior. Дополнительные сведения см. в разделе Использование триггеров событий для управления раскадровкой после ее запуска.For more information, see Use Event Triggers to Control a Storyboard After It Starts.

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

В этом разделе перенаправленные события рассматриваются, главным образом, с точки зрения описания основных понятий. Также приводится руководство по тому, как и когда следует отвечать на перенаправляемые события, которые уже существуют в различных базовых элементах и элементах управления.This topic mainly discusses routed events from the perspective of describing the basic concepts and offering guidance on how and when to respond to the routed events that are already present in the various base elements and controls. Однако можно создать собственное перенаправленное событие на пользовательском классе вместе со всей необходимой поддержкой, такой как особые для этого события классы данных и делегаты.However, you can create your own routed event on your custom class along with all the necessary support, such as specialized event data classes and delegates. Владельцем перенаправленного события может быть любой класс, но перенаправленные события должны быть вызваны и обработаны UIElement ContentElement производными классами, чтобы быть полезными.The routed event owner can be any class, but routed events must be raised by and handled by UIElement or ContentElement derived classes in order to be useful. Дополнительные сведения о пользовательских событиях см. в разделе Создание пользовательских событий маршрутизации.For more information about custom events, see Create a Custom Routed Event.

См. такжеSee also