Wpf 鼠标事件是否可以/应该冒泡?

Wpf 鼠标事件是否可以/应该冒泡?,wpf,wpf-controls,routed-events,Wpf,Wpf Controls,Routed Events,在任何情况下都可能使MouseEnter事件冒泡吗 MSDN表示,这是一个附带的带有直接路由策略的事件,技术上排除了这种可能性。我有一个相当复杂的控件(本质上是由网格、堆栈面板和内容控件组成的层次结构)。我似乎从下至上传播MouseEnter事件,下面是从OnMouseEnter处理程序获取的调试转储(我在层次结构的不同级别包含了相同的自定义控件,用于处理MouseEnter,因此我有一个中心位置来侦听该事件): 输入:父级:s7b,时间戳:37989609 输入:父项:s2,时间戳:37989

在任何情况下都可能使MouseEnter事件冒泡吗

MSDN表示,这是一个附带的带有直接路由策略的事件,技术上排除了这种可能性。我有一个相当复杂的控件(本质上是由网格、堆栈面板和内容控件组成的层次结构)。我似乎从下至上传播MouseEnter事件,下面是从OnMouseEnter处理程序获取的调试转储(我在层次结构的不同级别包含了相同的自定义控件,用于处理MouseEnter,因此我有一个中心位置来侦听该事件):

输入:父级:s7b,时间戳:37989609

输入:父项:s2,时间戳:37989609

在:父:根,时间戳:37989609

s7b、s2和Root是框架元素名称,timestamp是来自MosueEnter事件的timestamp

如果路由策略是直接的,WPF如何决定事件发起人?它是否遍历可视树,直到找到第一个带有附加MouseEnter事件的FrameworkElement

当我正在为这个问题做一个最低限度的重新设置时,有人能建议是什么导致了这种行为吗


这是报告:

  • 创建两个自定义控件,一个是contant控件,另一个是事件接收器
  • 1.1。霉菌控制

    代码:

    XAML:

    XAML:

    
    
  • 最后,我的测试工具的标记:
  • XAML:

    
    

    为了重现这个问题,只需将鼠标悬停在最右边的灰色方框上,并查看调试输出窗口,您将在其中看到三个条目,而我只希望看到一个条目


    干杯。

    因为这是一个复杂的控件,所以当您用鼠标输入根元素时,很可能同时也在输入s7b和s2。由于这三个元素都是为MouseEnter事件注册的,因此如果鼠标可以同时输入这三个元素,则它们应该完全同时响应

    该事件可能出现在可视化树上,因为您碰巧为一行大小相似的可视化父对象注册了MouseEnter。如果我在StackPanel中定义一个按钮,该按钮拉伸以填充StackPanel,并为MouseEnter事件注册这两个按钮,那么无论何时鼠标进入按钮,默认情况下它也将进入父级(StackPanel)。在这种情况下,事件可能看起来像是在可视化树上冒泡,而实际上它只是同时发生的两个独立元素的直接事件

    如果要创建复杂控件,通常需要对整个控件进行一次MouseEnter回调,或对控件的特定部分进行特定的MouseEnter回调。是否确实需要对整个控件以及控件的各个部分进行回调

    -编辑

    刚刚看到你的新帖子。我尝试了你的代码,我注意到内容MyContentControl实例都是嵌套的。由于MyContentControl类派生自content control,因此正在拉伸控件以适应可用空间。您可以通过向MyContentControl类添加border属性来看到这一点。由于MyContentControl的背景默认为空,因此只有在触摸其中一个灰色框时才会触发鼠标


    第一个MyContentControl创建一个水平堆叠面板,并添加灰色框,然后添加内容呈现器。网格右侧带有第一个灰色框的任何内容都将自动位于c2和/或c3中,因为c1中的内容呈现器将被拉伸以适合具有固定高度和宽度的窗口大小。这就是为什么当您将鼠标悬停在c2上时,会得到c1和c2的鼠标指针,因为当触摸灰色框时,鼠标已进入c1的内容呈现器,并且鼠标也已进入c2的灰色框。类似的逻辑可以用来理解c3的情况。

    鼠标透明控件(MTC)(我倾向于称之为布局控件)具有鼠标不透明子级(MOC)无法正确处理鼠标事件

    我可能错了,但在我看来这是个错误。我可以猜测,罪魁祸首是MTC无法处理鼠标输入,但却假装这样做相当不协调

    由于附加事件的优点,MTC成为鼠标事件的源和原始源,并且它们的IsMouseOver设置为true,这与系统的其他部分不太协调

    解决方法是-只订阅控件中鼠标不透明部分的鼠标事件。乍一看,这听起来很可怕,但如果您使用命令,您不应该失去太多的灵活性


    非常感谢您的建议。

    也许更详细的描述会有所帮助。在on
    Mouse.MouseEnter

    尽管此事件用于跟踪鼠标何时进入元素,但它也报告此元素的IsMouseOver属性已从false更改为true

    MSDN说当
    IsMouseOver
    从false变为true时,
    Mouse.MouseEnter
    会触发。查看for
    IsMouseOver
    的报价如下:

    获取一个值,该值指示鼠标指针是否位于此元素上(包括其边界内的可视子元素)

    我们都同意,空背景不支持交互。关于
    IsMouseOver
    ,有许多警告,但从实际应用中可以明显看出,该值不会在空背景下切换。但是,该定义确实指出,如果鼠标“位于”元素边界内的任何可视子对象上,则
    IsMouseOver
    将改变,除非出现几个奇怪的cav
        public class MyContentControl : ContentControl
        {
            static MyContentControl()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(MyContentControl), 
                    new FrameworkPropertyMetadata(typeof(MyContentControl)));
            }
    
            protected override void OnMouseEnter(MouseEventArgs e)
            {
                if (e.Source == e.OriginalSource
                    && e.Source is MyContentControl)
                {
                    Debug.Write(string.Format("mouseenter:{0}, timestamp:{1}\n",
                        (e.Source as MyContentControl).Name,
                        e.Timestamp));
                }
    
                base.OnMouseEnter(e);
            }
        }
    
    <Style TargetType="{x:Type local:MyContentControl}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type local:MyContentControl}">
                        <StackPanel Orientation="Horizontal">
                            <local:MouseEventReceiver />
                            <ContentPresenter />
                        </StackPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
    public class MouseEventReceiver : Control
    {
        static MouseEventReceiver()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MouseEventReceiver), 
                new FrameworkPropertyMetadata(typeof(MouseEventReceiver)));
        }
    }
    
    <Style TargetType="{x:Type local:MouseEventReceiver}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Grid Background="LightGray" Width="20" Height="20" Margin="5"></Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
    <Window x:Class="MouseTricks.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:MouseTricks"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <local:MyContentControl x:Name="c1">
                <local:MyContentControl x:Name="c2">
                    <local:MyContentControl x:Name="c3" />
                </local:MyContentControl>
            </local:MyContentControl>
        </Grid>
    </Window>
    
        private void MouseMove_Callback(Object sender, MouseEventArgs e)
        {
            if (c1.IsMouseOver)
                MessageBox.Show("Mouse is Over c1!");
        }