Обзор перенаправленных событий (WPF .NET)

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

Важно!

Документация по рабочему столу для .NET 7 и .NET 6 находится в стадии разработки.

Необходимые компоненты

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

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

Вы можете рассмотреть перенаправленные события с точки зрения функциональности или реализации:

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

  • С точки зрения реализации перенаправленное событие — это событие, зарегистрированное в системе событий WPF, поддерживаемое экземпляром RoutedEvent класса, и обработанное системой событий WPF. Как правило, перенаправленное событие реализуется с помощью события CLR "оболочка", чтобы включить присоединение обработчиков в XAML и в коде позади, как и событие CLR.

Приложения WPF обычно содержат множество элементов, объявленных в XAML или созданных в коде. Элементы приложения существуют в дереве элементов. В зависимости от того, как определяется перенаправленное событие, когда событие вызывается в исходном элементе:

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

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

<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">
    <StackPanel Background="LightBlue" Orientation="Horizontal" Button.Click="YesNoCancelButton_Click">
        <Button Name="YesButton">Yes</Button>
        <Button Name="NoButton">No</Button>
        <Button Name="CancelButton">Cancel</Button>
    </StackPanel>
</Border>

Дерево элементов отображается, как показано ниже.

Yes, No, and Cancel buttons.

Каждая из трех кнопок является потенциальным Click источником событий. При нажатии одной из кнопок вызывается событие, которое пузырится Click с кнопки на корневой элемент. У Button элементов Border нет вложенных обработчиков событий, но они выполняются StackPanel . Возможно, другие элементы выше в дереве, которые не отображаются, также Click подключены обработчики событий. Click Когда событие достигает StackPanel элемента, система событий WPF вызывает YesNoCancelButton_Click обработчик, подключенный к нему. Маршрут событий для Click события в примере: Button —>Border>StackPanel —> последовательные родительские элементы.

Примечание.

Элемент, который первоначально вызвал перенаправленное событие, определяется как RoutedEventArgs.Source в параметрах обработчика событий. Прослушиватель событий — это элемент, в котором обработчик событий подключен и вызван, и определяется как отправитель в параметрах обработчика событий.

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

Ниже приведены некоторые сценарии, которые мотивировали концепцию перенаправленного события и отличают ее от типичного события СРЕДЫ CLR:

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

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

    private void YesNoCancelButton_Click(object sender, RoutedEventArgs e)
    {
        FrameworkElement sourceFrameworkElement = e.Source as FrameworkElement;
        switch (sourceFrameworkElement.Name)
        {
            case "YesButton":
                // YesButton logic.
                break;
            case "NoButton":
                // NoButton logic.
                break;
            case "CancelButton":
                // CancelButton logic.
                break;
        }
        e.Handled = true;
    }
    
    Private Sub YesNoCancelButton_Click(sender As Object, e As RoutedEventArgs)
        Dim frameworkElementSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
    
        Select Case frameworkElementSource.Name
            Case "YesButton"
                ' YesButton logic.
            Case "NoButton"
                ' NoButton logic.
            Case "CancelButton"
                ' CancelButton logic.
        End Select
    
        e.Handled = True
    End Sub
    
  • Обработка классов. Перенаправленные события поддерживают обработчик событий класса, который определяется в классе. Обработчики классов обрабатывают событие перед любыми обработчиками экземпляров для одного и того же события в любом экземпляре класса.

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

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

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

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

// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    name: "Tap",
    routingStrategy: RoutingStrategy.Bubble,
    handlerType: typeof(RoutedEventHandler),
    ownerType: typeof(CustomButton));

// Provide CLR accessors for adding and removing an event handler.
public event RoutedEventHandler Tap
{
    add { AddHandler(TapEvent, value); }
    remove { RemoveHandler(TapEvent, value); }
}
' Register a custom routed event using the Bubble routing strategy.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
    name:="Tap",
    routingStrategy:=RoutingStrategy.Bubble,
    handlerType:=GetType(RoutedEventHandler),
    ownerType:=GetType(CustomButton))

' Provide CLR accessors for adding and removing an event handler.
Public Custom Event Tap As RoutedEventHandler
    AddHandler(value As RoutedEventHandler)
        [AddHandler](TapEvent, value)
    End AddHandler

    RemoveHandler(value As RoutedEventHandler)
        [RemoveHandler](TapEvent, value)
    End RemoveHandler

    RaiseEvent(sender As Object, e As RoutedEventArgs)
        [RaiseEvent](e)
    End RaiseEvent
End Event

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

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

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

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

  • Прямой: вызываются только обработчики событий в источнике событий. Эта стратегия не маршрутизации аналогична событиям платформы пользовательского интерфейса Windows Forms, которые являются стандартными событиями СРЕДЫ CLR. В отличие от событий СРЕДЫ CLR, прямые перенаправленные события поддерживают обработку классов и могут использоваться EventSetters и EventTriggers.

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

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

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

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

Помимо аспектов маршрутизации, вы можете реализовать перенаправленное событие вместо стандартного события СРЕДЫ CLR по следующим причинам:

  • Для некоторых функций стилизации и шаблонов WPF, таких как EventSetters и EventTriggers, требуется перенаправленное событие.

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

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

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

Для события, являющегося членом (унаследованным или иным образом) класса прослушивателя, можно подключить обработчик следующим образом:

<Button Name="Button1" Click="Button_Click">Click me</Button>

Если событие не является членом класса прослушивателя, необходимо использовать полное имя события в форме <owner type>.<event name>. Например, поскольку StackPanel класс не реализует Click событие, чтобы подключить обработчик к StackPanelClick событию, который пузырится до этого элемента, необходимо использовать синтаксис имени события:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

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

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Click event logic.
}
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Click event logic.
End Sub

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

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

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

  • Напрямую AddHandler вызовите метод. Перенаправленные обработчики событий всегда могут быть присоединены таким образом. В этом примере обработчик событий присоединяется Click к кнопке с помощью AddHandler метода:

    Button1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    Button1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    

    Чтобы подключить обработчик события кнопки Click к другому элементу маршрута события, например именованному StackPanelStackPanel1:

    StackPanel1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    StackPanel1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    
  • Если перенаправленное событие реализует оболочку событий СРЕДЫ CLR, используйте синтаксис событий для конкретного языка, чтобы добавить обработчики событий так же, как и для стандартного события CLR. Большинство существующих перенаправленных событий WPF реализуют оболочку СРЕДЫ CLR, что позволяет использовать синтаксис событий для конкретного языка. В этом примере обработчик событий присоединяется Click к кнопке с помощью определенного языка синтаксиса:

    Button1.Click += Button_Click;
    
    AddHandler Button1.Click, AddressOf Button_Click
    

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

Концепция обработанного

Все маршрутизируемые события используют общий базовый класс для данных событий, который является классом RoutedEventArgs . Класс RoutedEventArgs определяет логическое Handled свойство. Цель Handled свойства — разрешить любому обработчику событий по маршруту события пометить перенаправленное событие как обработанное. Чтобы пометить событие как обработанное, задайте значение Handledtrue в коде обработчика событий.

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

  • Подключите обработчик в коде позади с помощью перегрузки AddHandler(RoutedEvent, Delegate, Boolean) с заданным параметром handledEventsTootrue.

  • Задайте для атрибута HandledEventsToo значение EventSettertrue.

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

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

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

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

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

    • Запустите код в ответ на событие в той степени, в которой помечается событие как обработанное. Пометьте событие как обработанное в данных события. Событие по-прежнему направляется к следующему прослушивателю в дереве, но большинство прослушивателей не вызывает дополнительные обработчики. Исключение — прослушиватели с обработчиками, которые были специально зарегистрированы с handledEventsToo заданным значением true.

Дополнительные сведения об обработке перенаправленных событий см. в разделе "Маркировка перенаправленных событий как обработанные" и "Обработка классов".

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

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

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

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

Присоединенные события в WPF

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

В синтаксисе XAML присоединенное событие определяется его именем события и типом владельца в виде <owner type>.<event name>. Так как имя события квалифицировано с именем своего типа владельца, синтаксис позволяет присоединить событие к любому элементу, который можно создать. Этот синтаксис также применим к обработчикам регулярных перенаправленных событий, которые присоединяются к произвольному элементу вдоль маршрута событий. Кроме того, можно подключить обработчики для присоединенных событий в коде, вызвав AddHandler метод объекта, к которому должен подключиться обработчик.

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

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

Квалифицированные имена событий в XAML

Синтаксис <owner type>.<event name> квалифифисирует имя события с именем своего типа владельца. Этот синтаксис позволяет присоединять событие к любому элементу, а не только элементам, реализующим событие в качестве члена своего класса. Синтаксис применим при присоединении обработчиков в XAML для присоединенных событий или перенаправленных событий на произвольных элементах вдоль маршрута событий. Рассмотрим сценарий, в котором необходимо подключить обработчик к родительскому элементу, чтобы обрабатывать перенаправленные события, вызванные дочерними элементами. Если родительский элемент не имеет перенаправленного события в качестве члена, вам потребуется использовать синтаксис имени события. Например:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

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

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

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

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

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

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

Event routing diagram.

Порядок обработки событий после действия мыши на конечном элементе #2:

  1. PreviewMouseDown событие туннелирования в корневом элементе.
  2. PreviewMouseDown событие туннелирования на промежуточном элементе #1.
  3. PreviewMouseDown Событие туннелирования в конечном элементе #2, являющееся исходным элементом.
  4. MouseDown событие пузырька на конечном элементе #2, который является исходным элементом.
  5. MouseDown событие бублинга на промежуточном элементе #1.
  6. MouseDown событие пузырьков в корневом элементе.

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

Если обработчик событий ввода завершает логику, необходимую для решения события, следует пометить входное событие как обработанное. Как правило, после того как событие ввода помечается Handled, обработчики дальше по маршруту событий не вызываются. Однако обработчики событий ввода, зарегистрированные в наборе handledEventsTootrue параметров, будут вызываться, даже если событие помечается как обработанное. Дополнительные сведения см. в разделе "Предварительный просмотр событий" и "Маркировка перенаправленных событий" в качестве обработанных и обработки классов.

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

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

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

EventSetters и EventTriggers

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

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type Button}">
            <EventSetter Event="Click" Handler="ApplyButtonStyle"/>
        </Style>
    </StackPanel.Resources>
    <Button>Click me</Button>
    <Button Click="Button_Click">Click me</Button>
</StackPanel>

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

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

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

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

См. также