Прикрепляемые события

45

Рассмотренная декоративная метка из предыдущей статьи является довольно простым примером пузырькового распространения события, поскольку все элементы поддерживают событие MouseUp. Но многие элементы управления обладают собственными специальными событиями. Одним из таких примеров является кнопка: она добавляет событие Click, которое не определено ни в одном базовом классе.

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

К сожалению, следующий — вроде бы очевидный — код работать не будет:

<StackPanel Click="DoSomething" Margin="5">
   <Button Name="cmd1">Command 1</Button>
   <Button Name="cmd2">Command 2</Button>
   <Button Name="cmd3">Command 3</Button>
</StackPanel>

Дело в том, что StackPanel не содержит событие Click, поэтому такой код вызовет ошибку во время синтаксического анализа XAML. Для решения этой задачи нужно использовать другой синтаксис с применением прикрепленных событий в виде ИмяКласса.ИмяСобытия. Вот исправленный вариант:

<StackPanel Button.Click="DoSomething" Margin="5">
   <Button Name="cmd1">Command 1</Button>
   <Button Name="cmd2">Command 2</Button>
   <Button Name="cmd3">Command 3</Button>
</StackPanel>

Теперь обработчик события получит управление при щелчках на всех упакованных кнопках.

Событие Click определено в классе ButtonBase и наследуется классом Button. Если прикрепить обработчик события к ButtonBase.Click, то этот обработчик события будет использоваться при щелчке на любом элементе управления, порожденном от ButtonBase (включая классы Button, RadioButton и CheckBox). Но если прикрепить обработчик события к Button.Click, то он будет использоваться только для объектов Button.

Прикрепляемое событие можно подключить и в коде, но тогда вместо операции += придется использовать метод UIElement.AddHandler(). Вот пример (здесь предполагается, что элемент StackPanel имеет имя pnlButtons):

pnlButtons.AddHandler(Button.Click, new RoutedEventHandler(DoSomething));

Есть несколько возможностей определить в обработчике события DoSomething(), какая кнопка сгенерировала событие. Можно сравнить ее текст (возможны проблемы с локализацией) или ее имя (ненадежно, так как на этапе создания приложения невозможно перехватить ошибочно введенные имена). Лучше всего задать с помощью XAML у каждой кнопки свойство Name — тогда можно обратиться к соответствующему объекту посредством свойства OriginalSource класса RoutedEventArgs. Вот пример:

private void DoSomething(object sender, RoutedEventArgs e)
{
            if (e.OriginalSource == cmd1)
            {       }
            else if (e.OriginalSource == cmd2)
            {       }
            else if (e.OriginalSource == cmd3)
            {       }
}

Существует еще один вариант: вместе с кнопкой отправить порцию информации, которую можно использовать в коде. Например, для каждой кнопки можно задать свойство Tag:

<StackPanel Button.Click="DoSomething" Margin="5">
   <Button Name="cmd1" Tag="The first button.">Command 1</Button>
   <Button Name="cmd2" Tag="The second button.">Command 2</Button>
   <Button Name="cmd3" Tag="The third button.">Command 3</Button>
</StackPanel>

После этого можно обращаться к свойству Tag в коде:

private void DoSomething(object sender, RoutedEventArgs e)
{
    object tag = ((Button)e.OriginalSource).Tag;
    MessageBox.Show((string)tag);
}
Пройди тесты
Лучший чат для C# программистов