Button.MouseDown

时间:2010-06-05 21:25:14

标签: c# wpf routedevents

我对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 =&gt; Preview_B2 =&gt; Preview_B3)。 我的期望是: Preview_B1 =&GT; Preview_B2 =&GT; Preview_B3 =&GT; NonPreview_B3 =&GT; NonPreview_B2 =&GT; NonPreview_B1

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

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

了解这些事件是如何运作对我来说非常重要,我可能会错过 - 理解这里的基本知识,我们将非常感谢您的帮助。

THANX !! -Gili

4 个答案:

答案 0 :(得分:7)

由于Button控件的功能是生成Click事件(或触发命令),因此它对鼠标事件的处理方式与其他控件略有不同。当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气泡
  面板泡泡

答案 1 :(得分:2)

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

答案 2 :(得分:0)

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

谢谢!

答案 3 :(得分:0)

这个问题很老,但我在同一个地区工作,我可能会有答案。 John Bowen对此没有任何问题。他正在使用Debug trace语句进行测试。 那些遇到问题的人正在使用MessageBoxes进行测试。

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

相关问题