Как создать пользовательское перенаправленное событие (WPF .NET)

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

Важно!

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

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

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

Шаги по созданию перенаправленного события

Ниже приведены основные шаги для создания перенаправленного события:

  1. Зарегистрируйте RoutedEvent с помощью метода RegisterRoutedEvent.

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

    • Идентификатор перенаправленного события со стратегией восходящей маршрутизации событий называется <event name>Event. Например, если именем события является Tap, этот идентификатор должен называться TapEvent.
    • Идентификатор перенаправленного события со стратегией туннелирования называется Preview<event name>Event. Например, если именем события является Tap, этот идентификатор должен называться PreviewTapEvent.
  3. Определите методы доступа к событиям add и remove для среды CLR. Без методов доступа к событиям CLR вы сможете добавлять или удалять обработчики событий только с помощью прямых вызовов методов UIElement.AddHandler и UIElement.RemoveHandler. При использовании методов доступа к событиям CLR вы получаете следующие механизмы назначения обработчика событий:

    • В языке XAML можно использовать синтаксис атрибутов для добавления обработчиков событий.
    • В языке C# можно использовать операторы += и -= для добавления или удаления обработчиков событий.
    • В Visual Basic можно использовать инструкции AddHandler и RemoveHandler для добавления или удаления обработчиков событий.
  4. Добавьте пользовательскую логику для активации перенаправленного события. Например, ваша логика может активировать событие на основе данных, введенных пользователем, и состояния приложения.

Пример

В следующем примере класс CustomButton реализуется в пользовательской библиотеке элементов управления. Класс CustomButton, который является производным от Button:

  1. Регистрирует RoutedEvent с именем ConditionalClick с помощью метода RegisterRoutedEvent и задает стратегию восходящей маршрутизации событий во время регистрации.
  2. Назначает экземпляр RoutedEvent, возвращенный из вызова регистрации, в статическое поле только для чтения с именем ConditionalClickEvent.
  3. Определяет методы доступа к событиям add и remove для среды CLR.
  4. Добавляет пользовательскую логику для выдачи пользовательского перенаправленного события при щелчке CustomButton и применении внешнего условия. Хотя этот пример кода вызывает перенаправленное событие ConditionalClick из переопределенного виртуального метода OnClick, вы можете вызывать событие любым удобным способом.
public class CustomButton : Button
{
    // Register a custom routed event using the Bubble routing strategy.
    public static readonly RoutedEvent ConditionalClickEvent = EventManager.RegisterRoutedEvent(
        name: "ConditionalClick",
        routingStrategy: RoutingStrategy.Bubble,
        handlerType: typeof(RoutedEventHandler),
        ownerType: typeof(CustomButton));

    // Provide CLR accessors for assigning an event handler.
    public event RoutedEventHandler ConditionalClick
    {
        add { AddHandler(ConditionalClickEvent, value); }
        remove { RemoveHandler(ConditionalClickEvent, value); }
    }

    void RaiseCustomRoutedEvent()
    {
        // Create a RoutedEventArgs instance.
        RoutedEventArgs routedEventArgs = new(routedEvent: ConditionalClickEvent);

        // Raise the event, which will bubble up through the element tree.
        RaiseEvent(routedEventArgs);
    }

    // For demo purposes, we use the Click event as a trigger.
    protected override void OnClick()
    {
        // Some condition combined with the Click event will trigger the ConditionalClick event.
        if (DateTime.Now > new DateTime())
            RaiseCustomRoutedEvent();

        // Call the base class OnClick() method so Click event subscribers are notified.
        base.OnClick();
    }
}
Public Class CustomButton
    Inherits Button

    ' Register a custom routed event with the Bubble routing strategy.
    Public Shared ReadOnly ConditionalClickEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
        name:="ConditionalClick",
        routingStrategy:=RoutingStrategy.Bubble,
        handlerType:=GetType(RoutedEventHandler),
        ownerType:=GetType(CustomButton))

    ' Provide CLR accessors to support event handler assignment.
    Public Custom Event ConditionalClick As RoutedEventHandler

        AddHandler(value As RoutedEventHandler)
            [AddHandler](ConditionalClickEvent, value)
        End AddHandler

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

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

    End Event

    Private Sub RaiseCustomRoutedEvent()

        ' Create a RoutedEventArgs instance.
        Dim routedEventArgs As New RoutedEventArgs(routedEvent:=ConditionalClickEvent)

        ' Raise the event, which will bubble up through the element tree.
        [RaiseEvent](routedEventArgs)

    End Sub

    ' For demo purposes, we use the Click event as a trigger.
    Protected Overrides Sub OnClick()

        ' Some condition combined with the Click event will trigger the ConditionalClick event.
        If Date.Now > New DateTime() Then RaiseCustomRoutedEvent()

        ' Call the base class OnClick() method so Click event subscribers are notified.
        MyBase.OnClick()

    End Sub
End Class

Этот пример содержит отдельное приложение WPF, использующее разметку XAML для добавления экземпляра CustomButton в StackPanel, а также для назначения метода Handler_ConditionalClick в качестве обработчика событий ConditionalClick для элементов CustomButton и StackPanel1.

<Window x:Class="CodeSample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:WpfControl;assembly=WpfControlLibrary"
        Title="How to create a custom routed event" Height="100" Width="300">

    <StackPanel Name="StackPanel1" custom:CustomButton.ConditionalClick="Handler_ConditionalClick">
        <custom:CustomButton
            Name="customButton"
            ConditionalClick="Handler_ConditionalClick"
            Content="Click to trigger a custom routed event"
            Background="LightGray">
        </custom:CustomButton>
    </StackPanel>
</Window>

В коде программной части приложение WPF определяет метод обработчика событий Handler_ConditionalClick. Методы обработчика событий могут быть реализованы только в коде программной части.

// The ConditionalClick event handler.
private void Handler_ConditionalClick(object sender, RoutedEventArgs e)
{
    string senderName = ((FrameworkElement)sender).Name;
    string sourceName = ((FrameworkElement)e.Source).Name;

    Debug.WriteLine($"Routed event handler attached to {senderName}, " +
        $"triggered by the ConditionalClick routed event raised on {sourceName}.");
}

// Debug output when CustomButton is clicked:
// Routed event handler attached to CustomButton,
//     triggered by the ConditionalClick routed event raised on CustomButton.
// Routed event handler attached to StackPanel1,
//     triggered by the ConditionalClick routed event raised on CustomButton.
' The ConditionalClick event handler.
Private Sub Handler_ConditionalClick(sender As Object, e As RoutedEventArgs)

    Dim sourceName As String = CType(e.Source, FrameworkElement).Name
    Dim senderName As String = CType(sender, FrameworkElement).Name

    Debug.WriteLine($"Routed event handler attached to {senderName}, " +
        $"triggered by the ConditionalClick routed event raised on {sourceName}.")

End Sub

' Debug output when CustomButton is clicked:
' Routed event handler attached to CustomButton,
'     triggered by the ConditionalClick routed event raised on CustomButton.
' Routed event handler attached to StackPanel1,
'     triggered by the ConditionalClick routed event raised on CustomButton.

Если щелкнуть CustomButton:

  1. В CustomButton вызывается перенаправленное событие ConditionalClick.
  2. Активируется обработчик событий Handler_ConditionalClick, подключенный к CustomButton.
  3. Перенаправленное событие ConditionalClick перемещается вверх по дереву элементов к контейнеру StackPanel1.
  4. Активируется обработчик событий Handler_ConditionalClick, подключенный к StackPanel1.
  5. Перенаправленное событие ConditionalClick продолжается перемещаться вверх по дереву элементов. Потенциально оно может активировать другие обработчики событий ConditionalClick, подключенные к другим пройденным элементам.

Обработчик событий Handler_ConditionalClick получает следующие сведения о вызвавшее его событии:

  • Объект sender, являющийся элементом, к которому подключен обработчик событий. Обработчик sender запускается дляCustomButton первый раз, а для StackPanel1 второй раз.
  • Объект RoutedEventArgs.Source, являющийся элементом, который изначально вызвал событие. В этом примере Source всегда является CustomButton.

Примечание.

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

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

См. также