Обработка маршрутизируемого события

51

Прикрепить обработчик события можно несколькими способами. Чаще всего для этой цели добавляется атрибут события в разметку XAML. Данный атрибут события получает имя события, которое нужно обрабатывать, а его значением является имя метода обработчика события. Вот пример, в котором этот синтаксис применяется для прикрепления обработчика img_MouseUp к событию MouseUp элемента Image:

<Image Source="my_foto.jpg" Name="img" MouseUp="img_MouseUp"></Image>

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

<Button Click="cmdOKClick">OK</Button>

Может возникнуть желание прикрепить событие к высокоуровневому методу, выполняющему задачу. Однако вы получите большую гибкость при наличии дополнительного уровня кода для обработки событий. Например, щелчок на кнопке cmdUpdate не вызовет непосредственно метод UpdateDatabase(). Вместо этого будет вызван обработчик события — например, cmdUpdate_Click() — который может вызвать метод UpdateDatabase(), а уже тот сделает всю работу. Этот принцип позволяет изменить местонахождение кода базы данных, заменить кнопку обновления другим элементом управления, привязать несколько элементов управления к одному и тому же процессу — и все это при полной возможности изменять в последующем пользовательский интерфейс.

Если необходим более простой способ работы с действиями, которые могут запускаться из нескольких разных мест в пользовательском интерфейсе (кнопки панели инструментов, команды меню и т.д.), понадобится добавить средство команд WPF.

Событие можно соединить и с кодом. Вот эквивалент приведенного выше кода разметки XAML:

img.MouseUp += new MouseButtonEventHandler(img_MouseUp);

Этот код создает объект делегата, имеющий правильную сигнатуру для события (в данном случае это экземпляр делегата MouseButtonEventHandler) и указывающий на метод img_MouseUp(). Затем он добавляет делегат в список зарегистрированных обработчиков для события img.MouseUp.

Язык C# разрешает применять более лаконичный синтаксис, образом создающий подходящий объект делегата неявно:

img.MouseUp += img_MouseUp;

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

Подход, продемонстрированный в предыдущем коде, основан на оболочке события, которая вызывает метод UIElement.AddHandler(). Вы можете связать событие напрямую, самостоятельно вызвав метод UIElement.AddHandler(), например:

img.AddHandler(Image.MouseUpEvent,
   new MouseButtonEventHandler(img_MouseUp));

При использовании этого подхода всегда приходится создавать подходящий тип делегата (например, MouseButtonEventHandler). Нельзя создать объект делегата неявно, как при захвате события через оболочку свойства, поскольку метод UIElement.AddHandler() поддерживает все события WPF и не знает, какой тип делегата вы хотите использовать.

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

Выбор подхода зависит от ваших предпочтений. Хотя у второго подхода есть недостаток: он не дает ясного представления о том, что класс Image обеспечивает событие MouseUpEvent. Такой код можно неправильно понять и предположить, что он прикрепляет обработчик, предназначенный для обработки MouseUpEvent во вложенном элементе.

Если понадобится открепить обработчик события, то это можно сделать только в коде — например, с помощью операции -=:

img.MouseUp -= img_MouseUp;

Либо можно использовать метод UIElement.RemoveHandler():

img.RemoveHandler(Image.MouseUpEvent,
   new MouseButtonEventHandler(img_MouseUp));

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

Лучший чат для C# программистов