Когда в WPF используются события туннелирования и пузырения?

Я понимаю, как работают пузырьки и туннелирование. Тем не менее, я запутался в их использовании. Вот почему:

Я хочу обработать событие щелчка мыши. Чтобы пузырить его, есть MouseDown, и для его туннелирования есть PreviewMouseDown. Однако MouseDown не обязательно означает, что пользователь щелкнул элемент управления. Может быть, пользователь нажал кнопку и отошел от нее, чтобы отменить щелчок. Я бы не хотел ничего менять, если кнопка не нажата.

Итак, мой вопрос: насколько полезны стратегии Bubbling/Tunneling?

Ответ 1

Если событие указано RoutedEventArgs, то оно маршрутизирует событие. Маршрутизированные события поддерживают RoutingStrategy of Bubble, Tunnel или Direct. Посмотрим на обработчик событий Button.Click:

private void Grid_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Button Test clicked!");
}

Здесь указан RoutedEventArgs, поэтому он маршрутизирует событие. Поскольку предварительный просмотр не был указан в имени, поэтому это событие Bubble. Это можно продемонстрировать следующим образом:

<Grid ButtonBase.Click="Grid_Click">
    <Button Name="TestButton" Width="100" Height="30" Content="Test" />
</Grid>

Когда вы нажимаете на TestButton, событие должно подняться над Grid и отображает сообщение:

Нажмите кнопку Test!

Usefulness of Bubbling/Tunneling strategies

Tunneling

Многие стандартные элементы управления прослушивают события, такие как KeyDown, MouseDown и т.д. Например - DataGrid. Я хочу, нажав клавишу ввода, функция была вызвана добавлением записи. Но DataGrid уже имеет событие KeyDown, поэтому событие не возникает. Таким образом, вы должны выполнить свою логику в событии Tunnel - PreviewKeyDown, он будет работать до события KeyDown. То же самое относится к RichTextBoxControl.

Bubbling

Иногда вам нужен глобальный обработчик для определенного события, поэтому он работал для всех элементов управления в VisualTree. Естественно, прямое событие вы не можете этого сделать. Следовательно, на сцене появляется событие Bubbling.

Другая причина - идеология WPF. Этот Button может содержать все: Image, другое Button и т.д.:

enter image description here

Пользователь может щелкнуть по TextBlock/Image в Button. Как мы узнаем, что щелчок находился в Button? Это правильно, с помощью события Bubbling.

Для получения дополнительной информации см.

Общие сведения о маршрутизированных событиях и командах в WPF

Edit

Я немного изменил обработчик Click:

private void Grid_Click(object sender, RoutedEventArgs e)
{
    String message = "#" + eventCounter.ToString() + ":\r\n" +
            " Sender: " + sender.ToString() + ":\r\n" +
            " Source: " + e.Source + ":\r\n" +
            " Original Source: " + e.OriginalSource;

    lstEvents.Items.Add(message);
}

Результат щелчка по Button:

enter image description here

Ответ 2

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

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

Эта проблема будет намного хуже при использовании контента WPF   модель. Кнопка не ограничивается наличием открытого текста в виде заголовка - она ​​может содержать любые   объект как контент. Xaml ниже не особенно амбициозен, но даже   у этого есть шесть видимых элементов: желтый кружок, две точки для глаз,   кривая для рта, текста и самого фона кнопки. Присоединение события   обработчики для каждого отдельного элемента были бы утомительными и неэффективными. Чтобы работать с MouseDown Мы должны добавить 8 MouseDownEvents к этому фрагменту кода.

<Button PreviewMouseDown="PreviewMouseDownButton" MouseDown="MouseDownButton">
        <Grid PreviewMouseDown="PreviewMouseDownGrid" MouseDown="MouseDownGrid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Canvas PreviewMouseDown="PreviewMouseDownCanvas" MouseDown="MouseDownCanvas" Width="20" Height="18" VerticalAlignment="Center">
                <Ellipse PreviewMouseDown="PreviewMouseDownEllipse" MouseDown="MouseDownEllipse" x:Name="myEllipse" Canvas.Left="1" Canvas.Top="1" Width="16" Height="16" Fill="Yellow" Stroke="Black" />
                <Ellipse Canvas.Left="4.5" Canvas.Top="5" Width="2.5" MouseDown="MouseDownEllipse" Height="3" Fill="Black" />
                <Ellipse Canvas.Left="11" Canvas.Top="5" Width="2.5" MouseDown="MouseDownEllipse" Height="3" Fill="Black" />
                <Path Data="M 5,10 A 3,3 0 0 0 13,10" Stroke="Black" MouseDown="Path_MouseDown_1"/>
            </Canvas>
            <TextBlock Grid.Column="1" MouseDown="TextBlock_MouseDown_1">Click!</TextBlock>
        </Grid>
    </Button>

WPF использует RoutedEvents Bubble/Tunnel/Normal, которые более тщательно, чем обычные события. Вместо   только обработчиков вызовов, прикрепленных к элементу, который поднял событие, WPF ходит   дерева элементов пользовательского интерфейса, вызывая все обработчики для маршрутизируемого события, присоединенного к   любой node от исходного элемента вплоть до корня дерева пользовательского интерфейса.

Ответ 3

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

Спасибо