1

考虑为按钮添加 ClickEvent- 和 PreviewMouseLeftButtonDown-Handler

<Button x:Name="button"
    Click="Button_Click"
    PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown">
</Button>

单击按钮时,首先会触发 PreviewMouseLeftButtonDown,然后触发 Click-Event。

如果您e.Handled = true在 Preview...-Event 中设置,则不再处理 Click-Event。

但是,现在让我们考虑一下 MouseLeftButtonDownEvent。
首先,这个事件的路由策略是直接的。也就是说,每次控制都会重新加注。相反,Preview...-Event 是隧道式的,Click-Event 是冒泡的。
其次,添加 MouseLeftButtonDownEventHandler 仅在注册处理程序时才会成功,以便甚至为已处理的事件调用它,如以下代码摘录所示。

button.AddHandler(MouseLeftButtonDownEvent,
                  new MouseButtonEventHandler(Button_MouseLeftButtonDown),
                  true);

我编写了一个测试应用程序,有一个按钮,并为每个事件添加了一个处理程序。当调用事件处理程序时,它会将一些信息写入文本块。

  • 当我单击该按钮时,将调用所有三个事件处理程序。
  • 当我添加e.Handled = true到 Preview...-EventHandler 时,只会调用此事件处理程序。甚至 Mouse...-EventHandler 也没有被提升,尽管我已经设置UIElement.AddHandler handledEventsToo为 true。
  • 当我添加e.Handled = true到 Mouse...-EventHandler 时,会调用所有三个事件处理程序。

这对我来说没有任何意义。Mouse...-EventHandlers 不会影响 Click-EventHandlers,但 Preview...-EventHandlers 会影响 Mouse...- 和 Click-EventHandlers。
甚至“强制”处理鼠标事件失败...-EventHandler。

实际上,我从没想过不同类型的事件处理程序会相互影响。我的理解是,如果我有一个 Preview...-Event 和一个 Click-Event,它们是独立的。

那么,我错过了什么?


这是非常简单的示例代码:

XAML:

<DockPanel>
    <Border x:Name="border" DockPanel.Dock="Top" Height="50"
            BorderBrush="Gray" BorderThickness="1">
        <StackPanel x:Name="stackpanel" Background="LightGray"
                    Orientation="Horizontal" HorizontalAlignment="Center">
            <Button x:Name="button" Width="Auto" 
                    PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown">
                Click Me
            </Button>
        </StackPanel>
    </Border>
    <Border DockPanel.Dock="Bottom" BorderBrush="Gray" BorderThickness="1">
        <ScrollViewer>
            <TextBlock x:Name="textBlock" TextWrapping="Wrap"/>
        </ScrollViewer>
    </Border>
</DockPanel>

代码隐藏:

public MainWindow()
{
    InitializeComponent();

    button.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(Button_MouseLeftButtonDown), true);
    button.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click), true);
    stackpanel.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click), true /*false*/ );
}

private void Output(object sender, RoutedEventArgs e)
{
    textBlock.Text += "RoutedEvent: " + e.RoutedEvent + "\n";
    textBlock.Text += "Sender: " + sender + "\n";
    textBlock.Text += "Source: " + e.Source + "\n";
    textBlock.Text += "OriginalSource: " + e.OriginalSource + "\n" + "\n";
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    // e.Handled = true;
    Output(sender, e);
}

private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // e.Handled = true;
    Output(sender, e);
}

private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    Output(sender, e);
}
4

1 回答 1

2

我从没想过不同类型的事件处理程序会相互影响

您大部分是正确的,因为这种情况很少见,但您可以在 MSDN 上的Preview Events页面中找到答案。从链接页面:

例如,Windows Presentation Foundation (WPF) Button 抑制由 Button 或其复合元素引发的 MouseLeftButtonDown 和 MouseLeftButtonDown 冒泡事件,以便捕获鼠标并引发始终由 Button 本身引发的 Click 事件。事件及其数据仍沿路径继续,但由于 Button 将事件数据标记为已处理,因此仅调用专门指示它们应在handledEventsToo 情况下执行的事件的处理程序。

此外,你这样说:

当我将 e.Handled = true 添加到 Mouse...-EventHandler 时,将调用所有三个事件处理程序

这是意料之中的,因为e.Handled在冒泡事件处理程序中的设置不会做任何事情......在事件离开处理程序代码后没有任何东西会读取该值。e.Handled主要用于隧道事件处理程序以阻止事件进一步路由。同样,从链接页面:

特别是对于输入事件,预览事件还与等效的冒泡事件共享事件数据实例。如果您使用 Preview 事件类处理程序将输入事件标记为已处理,则不会调用冒泡输入事件类处理程序。或者,如果您使用 Preview 事件实例处理程序将事件标记为已处理,则通常不会调用冒泡事件的处理程序。

于 2014-08-06T09:57:52.307 回答