Общие сведения о вложенных событиях

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

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

Предполагается, что вы ознакомились с разделами Общие сведения о перенаправленных событиях и XAML в WPF.

Синтаксис присоединенных событий

Присоединенные события имеют синтаксис XAML и шаблон кодирования, который должен использоваться резервным кодом для поддержки использования присоединенных событий.

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

Например, ниже приведен синтаксис XAML для подключения обработчика для пользовательского присоединенного события NeedsCleaning.

<aqua:Aquarium Name="theAquarium" Height="600" Width="800" aqua:AquariumFilter.NeedsCleaning="WashMe"/>

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

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

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

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

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

Хотя WPF определяет количество присоединенных событий, ситуации, в которых вы будете использовать или обрабатывать присоединенное событие напрямую, очень ограничены. Как правило, присоединенное событие служит в качестве архитектурного элемента, однако затем оно передается в неприсоединенное (поддерживаемое "оболочкой" события CLR) перенаправленное событие.

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

Обработка присоединенного события в WPF

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

Как правило, присоединенное событие WPF не сильно отличается от перенаправленного события WPF. Различие заключается в выборе источника события и его представления классом в качестве члена (что также влияет на синтаксис обработчика XAML).

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

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

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

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

  • Метод AddEventNameHandler с двумя параметрами. Первый параметр — это экземпляр, к которому добавляется обработчик событий. Вторым параметром является добавляемый обработчик событий. Метод должен быть public и static, без возвращаемого значения.

  • Метод RemoveEventNameHandler с двумя параметрами. Первый параметр — это экземпляр, из которого удаляется обработчик событий. Вторым параметром является удаляемый обработчик событий. Метод должен быть public и static, без возвращаемого значения.

Метод доступа AddEventNameHandler упрощает обработку XAML, когда атрибуты обработчика присоединенного события объявляются для элемента. Методы AddEventNameHandler и RemoveEventNameHandler также обеспечивают доступ кода к хранилищу обработчика событий для присоединенного события.

Этот общий шаблон еще недостаточно точен для практической реализации в структуре, поскольку реализация любого заданного модуля чтения XAML может иметь разные схемы для идентификации базовых событий в поддерживающем языке и архитектуре. Это одна из причин, по которой WPF реализует присоединенные события как перенаправленные события: идентификатор, используемый для события (RoutedEvent), уже определен системой событий WPF. Кроме того, перенаправление события является естественным расширением реализации для концепции уровня языка XAML присоединенного события.

Реализация AddEventNameHandler для присоединенного события WPF состоит из вызова метода AddHandler с перенаправленным событием и обработчиком в качестве аргументов.

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

Например, следующий код определяет присоединенное событие NeedsCleaning в классе владельца Aquarium, используя стратегию присоединенных событий WPF для объявления присоединенного события как перенаправленного события.

public static readonly RoutedEvent NeedsCleaningEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(AquariumFilter));
public static void AddNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
public static void RemoveNeedsCleaningHandler(DependencyObject d, RoutedEventHandler handler)
{
    UIElement uie = d as UIElement;
    if (uie != null)
    {
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler);
    }
}
Public Shared ReadOnly NeedsCleaningEvent As RoutedEvent = EventManager.RegisterRoutedEvent("NeedsCleaning", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(AquariumFilter))
Public Shared Sub AddNeedsCleaningHandler(ByVal d As DependencyObject, ByVal handler As RoutedEventHandler)
    Dim uie As UIElement = TryCast(d, UIElement)
    If uie IsNot Nothing Then
        uie.AddHandler(AquariumFilter.NeedsCleaningEvent, handler)
    End If
End Sub
Public Shared Sub RemoveNeedsCleaningHandler(ByVal d As DependencyObject, ByVal handler As RoutedEventHandler)
    Dim uie As UIElement = TryCast(d, UIElement)
    If uie IsNot Nothing Then
        uie.RemoveHandler(AquariumFilter.NeedsCleaningEvent, handler)
    End If
End Sub

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

Вызов присоединенного события WPF

Как правило, вам не требуется вызывать существующие определенные присоединенные события WPF в коде. Эти события соответствуют общей концептуальной модели "службы", и классы служб, такие как InputManager, отвечают за вызов событий.

Однако при определении пользовательского присоединенного события на основе модели WPF, подразумевающей создание присоединенных событий на базе RoutedEvent, можно использовать RaiseEvent для вызова присоединенного события из любого элемента UIElement или ContentElement. Вызов перенаправленного события (присоединенного или нет) требует объявления конкретного элемента в дереве элементов в качестве источника события. Этот источник указывается как вызывающий объект RaiseEvent. За определение того, какой элемент передается как источник в дереве, отвечает служба.

См. также