Пузырьковые события

34

Ниже представлен пример программы демонстрирующей пузырьковое распространение событий:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        MouseUp="Some_Clicked">
    <Grid Margin="3" MouseUp="Some_Clicked">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Label BorderBrush="LightBlue" BorderThickness="3"  HorizontalAlignment="Center" MouseUp="Some_Clicked">
            <StackPanel MouseUp="Some_Clicked">
                <TextBlock Margin="3" FontSize="13" MouseUp="Some_Clicked">
                Всем привет!
                </TextBlock>
                <Image Source="grimace.png" Width="60" Height="60" MouseUp="Some_Clicked"></Image>
                <TextBlock Margin="3" FontSize="13" MouseUp="Some_Clicked">
                Маршрутизация событий
                </TextBlock>
            </StackPanel>
        </Label>
        <ListBox Grid.Row="1" Margin="3" Name="lbInfo"></ListBox>
        <CheckBox Grid.Row="2" Margin="3" Content="Показать первое событие"
                  Name="chb_ShowFirstEvent"></CheckBox>
        <Button Grid.Row="3" Margin="3" Padding="3" HorizontalAlignment="Right"
                Click="Button_Click">Очистить</Button>
    </Grid>
</Window>
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        int i = 0;
        private void Some_Clicked(object sender, MouseButtonEventArgs e)
        {
            i++;
            string message = "--> " + i + ":\r\n" +
                "Объект: " + sender.ToString() + "\r\n" + 
                "Источник: " + e.Source.ToString() + "\r\n" + 
                "Начальный источник: " + e.OriginalSource;
            lbInfo.Items.Add(message);
            e.Handled = (bool)chb_ShowFirstEvent.IsChecked;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            lbInfo.Items.Clear();
        }
}

В данном примере создается простое окно, которое демонстрирует пузырьковое распространение события. Если щелкнуть на какой-либо части метки, события будут возникать в порядке, перечисленном на текстовой панели ниже. На рисунке приведен вид этого окна сразу после щелчка пользователя на изображении внутри метки. Событие MouseUp проходит пять уровней и останавливается на окне MainWindow.

Пузырьковое распространение события

С технической точки зрения событие MouseUp предоставляет объект MouseButtonEventArgs с дополнительной информацией о состоянии мыши в момент возникновения события. Однако класс MouseButtonEventArgs является наследником MouseEventArgs, который в свою очередь является наследником класса RoutedEventArgs. Это позволяет использовать его при объявлении обработчика события (как показано здесь), если дополнительная информация о мыши не требуется.

В этом примере есть еще один момент. Если установить флажок chb_ShowFirstEvent, метод Some_Clicked() присвоит свойству RoutedEventArgs.Handled значение true, что останавливает последовательность пузырькового распространения события сразу при его возникновении. Поэтому вы увидите в списке только первое событие, как показано на рисунке:

Отработанное событие

Здесь нужно дополнительное приведение, т.к. свойство CheckBox.IsChecked является логическим значением, которое может принимать значение null (bool?, а не bool). Значение null представляет неопределенное состояние флажка, которое означает, что он и не установлен, и не сброшен. Эта особенность не используется в данном примере, поэтому достаточно простого приведения.

Поскольку метод Some_Clicked() обрабатывает событие MouseUp, которое возникает в объекте Window, щелчки можно перехватывать в текстовой панели и на пустой поверхности окна. Однако событие MouseUp не возникает при щелчке на кнопке "Очистить" (которая удаляет из текстовой панели все записи). Это связано с тем, что кнопке соответствует интересный фрагмент кода, который блокирует событие MouseUp и генерирует событие более высокого уровня Click. Одновременно флагу Handled присваивается значение true, что блокирует дальнейшее продвижение события MouseUp.

В отличие от элементов управления Windows Forms, большинство элементов WPF не имеют события Click. Вместо этого у них есть более простые события MouseDown и MouseUp. Событие Click зарезервировано для кнопочных элементов управления.

Обработка заблокированного события

Интересно, что существует способ получать события, которые отмечены как обработанные. Вместо прикрепления обработчика события посредством XAML следует использовать рассмотренный ранее метод AddHandler(). Этот метод имеет перегруженный вариант, который принимает логическое значение в третьем параметре. Если задать его равным true, вы получите событие, даже если для него был установлен флаг Handled:

cmdClear.AddHandler(UIElement.MouseUpEvent,
   new MouseButtonEventHandler(cmdClear_MouseUp), true);

Такое решение редко бывает удачным. Кнопка предназначена для блокирования события MouseUp по очень простой причине: чтобы избежать путаницы. Ведь в Windows принято, что "щелкнуть" на кнопке можно и с помощью клавиатуры, да еще несколькими способами. Если вы ошибочно будете обрабатывать в элементе Button событие MouseUp, а не события Click, то ваш код будет реагировать только на щелчки мышью, но не на эквивалентные клавиатурные действия.

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