8

我对 WPF 比较陌生。我试图了解 MouseDownEvent 和 PreviewMouseDownEvent 之间的区别。

我了解 WPF 事件策略,并且我了解 MouseDown 事件是一个冒泡事件,而 PreviewMouseDown 是一个隧道事件。

我也了解这些事件被触发的顺序 - 根据这个 MSDN 概述http://msdn.microsoft.com/en-us/library/ms742806.aspx#routing(那里有一个带有示例的图表)。

所以我尝试编写一些自己的代码,例如检查一下:

<Grid x:Name="grid" Width="250">
    <StackPanel Mouse.MouseDown="StackPanel_MouseDown" PreviewMouseDown="StackPanel_PreviewMouseDown">
    <WPFVisualizerExample:MyButton x:Name="B1" PreviewMouseDown="B1_PreviewMouseDown" MouseDown="B1_MouseDown" Margin="5,5,5,5">
            <WPFVisualizerExample:MyButton x:Name="B2" PreviewMouseDown="B2_PreviewMouseDown" MouseDown="B2_MouseDown"  Margin="5,5,5,5">
                <WPFVisualizerExample:MyButton x:Name="B3" PreviewMouseDown="B3_PreviewMouseDown" MouseDown="B3_MouseDown"  Margin="5,5,5,5">Click Me</WPFVisualizerExample:MyButton>
            </WPFVisualizerExample:MyButton>
    </WPFVisualizerExample:MyButton>           
    </StackPanel>        
</Grid>

我对每个事件(预览和非预览)都有一个事件处理程序,我想看看发生了什么,哪个事件被抛出(我为每个事件显示了一个消息框)。

'MyButton' 用户控件只是扩展了基本 Button 并覆盖 OnMouseDown 和 OnPreviewMouseDown 以将 e.Handled 设置为 false:

    protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)
    {            
        base.OnMouseDown(e);
        e.Handled = false;
    }

    protected override void OnPreviewMouseDown(System.Windows.Input.MouseButtonEventArgs e)
    {            
        base.OnPreviewMouseDown(e);
        e.Handled = false;
    }

(尝试了这个和没有这个)。

根据 MSDN 概述(在上面的链接中),如果我有 3 个元素,那么事件路由应如下所示:

根元素上的 PreviewMouseDown(隧道)。

中间元素 #1 上的 PreviewMouseDown(隧道)。

源元素 #2 上的 PreviewMouseDown(隧道)。

源元素 #2 上的 MouseDown(气泡)。

中间元素 #1 上的 MouseDown(气泡)。

根元素上的 MouseDown(气泡)。

所以我希望消息框按照上面的方式显示。由于某种原因 - 我不明白只有预览事件被抛出(根据 MSDN 所说的 Preview_B1=>Preview_B2=>Preview_B3)。我的期望是:Preview_B1=>Preview_B2=>Preview_B3=>NonPreview_B3=>NonPreview_B2=>NonPreview_B1。

但是根本没有抛出非预览事件。

所以基本上我不了解事件的路线,从 MSDN 概述中我了解到路线从根元素开始,向下(隧道)到源元素,然后备份(气泡)到根元素,但是这个不是在实践中发生的事情。

了解这些事件是如何运作的对我来说非常重要,我可能在这里错过了一些基本的东西,我们将不胜感激。

谢谢!!-吉利

4

4 回答 4

7

由于 Button 控件的功能是产生一个 Click 事件(或触发一个命令),它对 Mouse 事件的处理与其他控件略有不同。当 Button 接收到 PreviewMouseDown 事件时,它会将其转换为 Click 事件,从而取消任何进一步的 Preview 隧道和 MouseDown 冒泡事件。这取决于 Button 的 ClickMode 设置。如果您更愿意自己使用鼠标事件,您可以只使用 ContentControl(尝试一下,您应该会看到您所期望的),但您可能会发现在大多数情况下使用 Button.Click 更容易。

更新

您的代码中一定有其他原因导致事件路由停止。这是一个与您的示例类似的示例,我验证了该示例显示了预期的输出(添加了一些样式以使元素明显):

<Window.Resources>
    <Style TargetType="{x:Type ContentControl}">
        <Setter Property="BorderThickness" Value="5" />
        <Setter Property="Padding" Value="10" />
        <Setter Property="Background" Value="PaleGreen" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ContentControl}">
                    <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
                        <ContentPresenter/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<StackPanel Tag="Panel" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown">
    <ContentControl BorderBrush="Red" Tag="C1" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown">
        <ContentControl BorderBrush="Green" Tag="C2" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown">
            <ContentControl Content="Click Me" BorderBrush="Blue" Tag="C3" PreviewMouseDown="PreviewMouseDown" MouseDown="MouseDown"/>
        </ContentControl>
    </ContentControl>
</StackPanel>

以及写入 Debug 的简单处理程序:

private void PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    Debug.WriteLine((sender as FrameworkElement).Tag + " Preview");
}

private void MouseDown(object sender, MouseButtonEventArgs e)
{
    Debug.WriteLine((sender as FrameworkElement).Tag + " Bubble");
}

调试输出(如果单击中心文本)是:

面板预览
C1 预览
C2 预览
C3 预览
C3 气泡
C2 气泡
C1 气泡
面板气泡

于 2010-06-05T22:27:02.300 回答
2

我现在正在研究路由事件概念,在我的测试应用程序中,主要区别在于MessageBox.Show()方法的滥用。似乎当您使用它来通知正在引发的事件时,首先PreviewMouseDown会引发所有调谐事件并显示适当的消息框,并且它们获得焦点的事实中止或处理冒泡MouseDown事件。在给定的代码示例中,我们可以更改通知方式(例如Debug.WriteLine,在 John 的示例中或更新一些绑定到 some 的字符串依赖属性TextBox.Text

于 2012-11-26T08:49:43.323 回答
0

阿松是对的!我有完全相同的问题。我创建了一个按钮并注册了 MouseDown 和 PreviewMouseDown-Event。在事件处理程序中,我刚刚显示了一个用于调试目的的消息框。我一直想知道为什么我只得到 PreviewMouseDown-Event,而不是 MouseDown-Event。原因是消息框。所以我将 MessageBox 更改为 System.Diagnostic.WriteLine 并且一切正常:)

谢谢!

于 2013-02-18T17:34:31.737 回答
0

这个问题很老,但我在同一领域工作,我可能会有答案。John Bowen 对此没有任何意见。他正在使用调试跟踪语句进行测试。那些对此有问题的人正在使用 MessageBoxes 进行测试。

这就是问题所在:鼠标按下按钮设置焦点并捕获鼠标。但是如果你显示一个 MessageBox,它会窃取焦点和鼠标捕获。那时,按钮真的很混乱,因为鼠标在它上面,但它没有捕获或焦点。在那之后,您不会期望鼠标消息的正常进展。老鼠被带走了!

于 2017-08-28T19:31:46.623 回答