在任何情况下都可以让MouseEnter事件冒泡吗?
MSDN表示这是一个附带直接路由策略的附加事件,技术上排除了这种可能性。我有一个相当复杂的控件(实质上是由网格,堆栈面板和内容控件组成的层次结构)。我似乎从底部向上传播MouseEnter事件,这是从OnMouseEnter处理程序获取的调试转储(我在层次结构的不同级别包含相同的自定义控件,它处理MouseEnter,所以我有一个监听该事件的中心位置) :
在:父母:s7b,时间戳:37989609
在:父:s2,时间戳:37989609
In:parent:Root,timestamp:37989609
s7b,s2和Root是FrameworkElement名称,时间戳是来自MosueEnter事件的e.Timestamp。
如果路由策略是直接的,WPF如何决定事件发起者?它是否遍历可视树,直到找到附加了MouseEnter事件的第一个FrameworkElement?
当我正在为这个问题制作一个简约的复制品时,有人会建议可能导致这种行为吗?
这是复制品:
1.1。 MyContentControl
代码:
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);
}
}
XAML:
<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>
1.2 MouseEventReceiver
代码:
public class MouseEventReceiver : Control
{
static MouseEventReceiver()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MouseEventReceiver),
new FrameworkPropertyMetadata(typeof(MouseEventReceiver)));
}
}
XAML:
<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>
XAML:
<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>
为了重现问题,只需将鼠标悬停在最右侧的灰色方块上并观察“调试输出”窗口,您将看到三个条目,而我只期待一个。
干杯。
答案 0 :(得分:2)
也许更详细的描述会有所帮助。在Mouse.MouseEnter
Mouse.MouseEnter
上,引用了以下内容:
虽然此事件用于跟踪鼠标何时进入元素,但它也报告IsMouseOver属性已在此元素上从false更改为true
MSDN表示IsMouseOver
在IsMouseOver
从false更改为true时触发。查看IsMouseOver
IsMouseOver
以下引用:
获取一个值,该值指示鼠标指针是否位于此元素上(包括其边界内的可视子元素)
正如我们都同意的那样,空背景不支持交互。关于VisualTreeHelper
,MSDN article有很多警告,但从实际应用中可以明显看出,这个值不会为空背景切换。但是,该定义确实说明如果鼠标“位于”元素范围内的任何视觉子元素之上,那么c1
将改变禁止几个奇怪的警告。但是,空背景不是这些警告之一。
使用MSDN article或c2
快速查看控件的可视树,显示所有三个灰色网格都是c3
的视觉子项,最右边的两个网格是可视化子项IsMouseOver
的最右边网格是c1
的视觉子节点。这是预期的,因为所有内容控件都嵌套在一起。
通过监控 private void MouseMove_Callback(Object sender, MouseEventArgs e)
{
if (c1.IsMouseOver)
MessageBox.Show("Mouse is Over c1!");
}
IsMouseOver
属性,您可以轻松看到当鼠标触及灰色方块时,属性值会更改为true。您可以通过向主窗口的鼠标移动事件添加回调来验证这一点。我使用了以下回调:
c1
您会注意到,无论您位于IsMouseOver
c1
的三个灰色方块中的哪一个都设置为true。这表示MouseEnter
在c1
超过三个方格中的任意一个时变为true,因此MSDN声明的声明为真。无论您触摸哪个灰色方块,c1
都会MouseEnter
被解雇,因为所有三个灰色方块都位于{{1}}视觉树中,并且未通过警告从鼠标命中测试中消除(例如null背景警告。)
正如MSDN声称的那样,{{1}}事件作为应用程序中的直接事件进行响应。
答案 1 :(得分:1)
由于这是一个复杂的控件,因此当您使用鼠标输入Root元素时,您似乎也可以同时输入s7b和s2。由于所有三个元素都是为MouseEnter事件注册的,如果鼠标可以同时输入所有三个元素,它们应该完全同时响应。
可能看起来该事件正在冒泡视觉树,因为您碰巧为一系列类似大小的视觉父项注册了MouseEnter。如果我在StackPanel中定义一个Button,按钮拉伸以填充StackPanel并为MouseEnter事件注册,那么每当鼠标进入Button时,默认情况下它也将进入父(StackPanel)。在这种情况下,它可能看起来像事件正在冒泡视觉树,而实际上它只是同时发生的两个独立元素的直接事件。
如果您正在创建一个复杂的控件,那么通常您需要一个MouseEnter回调用于整个控件或特定的MouseEnter回调用于控件的特定部分。您确定需要对整个控件以及控件的各个部分进行回调吗?
- 编辑
刚看到你的新帖子。我尝试了你的代码,我注意到内容MyContentControl实例都是嵌套的。由于MyContentControl类派生自内容控件,因此正在拉伸控件以适应可用空间。您可以通过向MyContentControl类添加border属性来查看此内容。由于默认情况下MyContentControl的背景为null,因此只有触摸其中一个灰色框时才会触发MouseEnter。
第一个MyContentControl创建一个水平Stackpanel并添加灰色框,然后添加内容呈现器。带有第一个灰色框的网格右侧的任何内容都将自动位于c2和/或c3中,因为来自c1的内容展示器将被拉伸以适合具有固定高度和宽度的窗口的大小。这就是为什么当你将鼠标悬停在c2上时,你会获得c1和c2的MouseEnter,因为当触摸灰色框时,鼠标已经进入了c1的内容演示者,鼠标也进入了c2的灰色框。类似的逻辑可用于理解c3的情况。
答案 2 :(得分:0)
鼠标透明控件(MTC)(我倾向于将它们称为布局控件)具有鼠标不透明的孩子(MOC)无法正确处理鼠标事件。
我可能错了,但对我来说这看起来像个错误。我可以猜测,罪魁祸首是MTC无法处理鼠标输入,但假装这样做是相当不协调的。
由于附加事件的优点,MTC成为Source&amp;鼠标事件的原始源,也将它们的IsMouseOver设置为true,这与系统的其他部分无法相处。
解决方法是 - 只为鼠标事件订阅控件的鼠标不透明部分。乍一看听起来很可怕,但是如果你使用命令,你就不应该失去很大的灵活性。
非常感谢任何建议。