C# 为什么';t';IsMouseDirectlyOverChanged';在窗口或控件上移动鼠标时触发?
这有点让我抓狂。我试图检测用户的鼠标何时在C# 为什么';t';IsMouseDirectlyOverChanged';在窗口或控件上移动鼠标时触发?,c#,wpf,C#,Wpf,这有点让我抓狂。我试图检测用户的鼠标何时在窗口的客户端区域上,这样我就可以运行一些特定的代码(即,我不仅仅是在样式触发器中执行某些操作) 这是我的密码 XAML: …但事件从未发生过 为了查看它是否是特定于窗口的内容,我将事件附加到按钮上,如下所示 TestButton.IsMouseDirectlyOverChanged += MainWindow_IsMouseDirectlyOverChanged; Loaded += MainWindow_Loaded; ClientArea.IsM
窗口的客户端区域上,这样我就可以运行一些特定的代码(即,我不仅仅是在样式触发器中执行某些操作)
这是我的密码
XAML:
…但事件从未发生过
为了查看它是否是特定于窗口的内容,我将事件附加到按钮上,如下所示
TestButton.IsMouseDirectlyOverChanged += MainWindow_IsMouseDirectlyOverChanged;
Loaded += MainWindow_Loaded;
ClientArea.IsMouseDirectlyOverChanged += ClientArea_IsMouseDirectlyOverChanged;
它现在启动,但只有当我实际点击按钮时,而不是当我悬停在按钮上方时
那么,当我直接悬停在客户机区域上时,如何检测?来自MSDN
备注:与IsMouseOver不同,只有当鼠标指针位于文本元素上时,此属性才为真,就像用于命中测试一样。如果鼠标指针位于子元素上,特别是位于作为元素的深层模板和合成的一部分的元素上,则此属性将为false
我想这解释了为什么它不会在窗口上启动,我无法解释为什么它会在你点击按钮时启动(而不是在鼠标悬停时)
那么MouseEnter
/MouseLeave
事件和IsMouseOver
属性呢?好的,我想出来了。它的工作原理与人们预期的不同,因为鼠标是对视觉树而不是逻辑树做出响应的
初步尝试
我首先为窗口
连接了一个MouseMove
事件(在逻辑树上工作),并在处理程序中添加了以下内容,从而发现了这一点:
Console.WriteLine($"The mouse is over '{System.Windows.Input.Mouse.DirectlyOver}'");
在控制台中,我看到它实际上位于边框
控件上方,而不是我所期望的窗口
。这很有意义,因为边框是窗口的默认模板的一部分
接下来,我打开Snoop,看了看可视化树,果然,窗口和按钮之间是边框(还有一些其他东西。)好的,简单。。。我将遍历视觉树,从按钮
到边框
但是。。。在窗口
的构造过程中,按钮
尚未具有可视父级,因为窗口
的模板尚未加载!(由于某种原因,即使在调用base.OnApplyTemplate
之后,您也无法在OnApplyTemplate
覆盖中找到它,但我跑题了。)
所以,知道它肯定会在加载的事件中出现,我就这样把它连接起来
TestButton.IsMouseDirectlyOverChanged += MainWindow_IsMouseDirectlyOverChanged;
Loaded += MainWindow_Loaded;
ClientArea.IsMouseDirectlyOverChanged += ClientArea_IsMouseDirectlyOverChanged;
在处理程序中,我做了这个
private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
Loaded -= MainWindow_Loaded;
var parent = (DependencyObject)TestButton;
while(parent != null && !(parent is Border))
parent = VisualTreeHelper.GetParent(parent);
(parent as Border).IsMouseDirectlyOverChanged += MainWindow_IsMouseDirectlyOverChanged;
}
一旦我这么做了,我就得到了我所期望的行为。我终于明白了
清晰和简单
然而,我一点也不喜欢这个,因为它不仅看起来笨拙而冗长,而且它还依赖于窗口的模板。不好
然后我挨了一巴掌。答案非常简单,我几乎感到尴尬:
<Window x:Class="SystemDialogPlayground.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="450" WindowStartupLocation="CenterScreen"
Title="Test Window">
<!-- Add your own border you can track against -->
<Border x:Name="ClientArea" Background="Transparent">
<Button x:Name="TestButton" Content="I Don't Do Anything"
Padding="16"
FontSize="24"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</Window>
这与“笨拙”的方式完全相同,但更简单、更干净。。。无需在树上行走,且基于模板不易碎
有时,我们程序员会惊讶地发现,我们会用多少左撇子才能变为右撇子。今晚我在做甜甜圈
重要提示:设置背景!
确保为您的边框提供背景,即使只设置为透明,就像我在这里所做的那样。这是因为如果背景为空,则背景不存在,因此鼠标不会“结束”,因此不会获得任何鼠标事件。将其设置为Transparent
可以简单地解决这一问题,而不会改变任何视觉效果
希望这有帮助 不,这并不能完全解释它,因为窗口是一个元素。另外,这个按钮不起作用。需要注意的是,WM\u NCHITTEST
消息按预期方式发送,因此我知道鼠标正在客户端区域注册。使用它的问题在于Win32子系统没有子系统,因为WPF在主窗口上呈现(不同于GDI+应用程序,其中每个子系统都是自己的窗口),因此,无法将WM\u nchitest
与HTCLIENT
一起用于WPF应用程序。我尝试了MouseEnter
/MouseLeave
,但它们也不起作用。我能请你试试我上面的代码吗?我使用的是虚拟机,从过去的经验中我知道,有时候这就是鼠标问题的原因。但是没有一个直接的Windows机器,我不能确定。我试过你的代码,它的行为和你描述的完全一样。好的。。。多恩!现在我明白了,这个答案很有道理。看看我刚刚发布的内容。事情太简单了,我几乎感到尴尬,因为我没有早点想到这一点我只是有另一个想法(我还没有机会检查)。我认为,当你点击事件时,它会在按钮上触发的原因是,当你点击它时,它会更改模板,并且在模板被交换出去的瞬间,鼠标可能直接位于可视树中按钮的根上(即按钮!)所以它开火了。这只是一个猜测,但确实有道理。