Kinda对此my之以鼻。我正在尝试检测用户的鼠标何时位于Window
的客户区域上方,以便我可以运行一些特定的代码(即,我不仅仅是在样式触发器中做某事。)
这是我的代码...
XAML:
<Window x:Class="Playground.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">
<Button x:Name="TestButton" Content="I Don't Do Anything"
Padding="16"
FontSize="24"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Window>
隐藏代码:
public partial class MainWindow {
public MainWindow(){
InitializeComponent();
IsMouseDirectlyOverChanged += MainWindow_IsMouseDirectlyOverChanged;
}
private void MainWindow_IsMouseDirectlyOverChanged(object sender, DependencyPropertyChangedEventArgs e) {
Console.WriteLine(nameof(MainWindow_IsMouseDirectlyOverChanged));
}
}
...但是事件永远不会触发。
要查看它是否特定于窗口,我将事件附加到按钮上,就像这样……
TestButton.IsMouseDirectlyOverChanged += MainWindow_IsMouseDirectlyOverChanged;
它现在会触发,但是只有当我实际单击该按钮时才会触发,而当我将鼠标悬停在该按钮上时不会触发。
那么当我将鼠标悬停在客户区域上方时,如何检测?
答案 0 :(得分:0)
UIElement.IsMouseDirectlyOver property来自MSDN。
备注:与IsMouseOver不同,此属性仅在鼠标指针位于文字元素上方时才为true,因为它是用于命中测试的。相反,如果鼠标指针悬停在子元素之上,尤其是在元素更深层模板和合成的一部分之上,则此属性为false。
我认为这解释了为什么它没有在窗口上触发,我无法解释为什么当您单击按钮时(而不是将鼠标悬停在其上)触发。
MouseEnter
/ MouseLeave
事件和IsMouseOver
属性如何?
答案 1 :(得分:0)
好,我知道了。它不能像人们期望的那样正常工作,因为鼠标响应的是可视树,而不是逻辑树。
我首先通过为MouseMove
连接一个Window
事件(在逻辑树上工作)来发现这一点,然后在处理程序中添加了以下内容:
Console.WriteLine($"The mouse is over '{System.Windows.Input.Mouse.DirectlyOver}'");
在控制台中,我看到它实际上是在Border
控件上,而不是我期望的Window
。这是有道理的,因为Border
是Window
的默认模板的一部分。
接下来,我打开了Snoop,看了一眼视觉树,果然在Window
和Button
之间是Border
(还有其他一些事情。) ,简单...我将把视觉树从Button
转到Border
。
但是...在Window
的构造函数中,Button
尚未具有可视父级,因为尚未加载Window
的模板! (由于某种原因,即使调用了OnApplyTemplate
但我离题了,也无法通过base.OnApplyTemplate
覆盖来实现它。)
所以,知道在Loaded
事件期间肯定会在那里,我像这样把它钩住了……
Loaded += MainWindow_Loaded;
在处理程序中,我做到了...
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
永不改变的模板。不好!</ p>
那我有一巴掌的时刻。答案是如此愚蠢,简直令我尴尬:
<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">
<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>
是的,就像添加我自己的边框一样简单,然后我像这样连接起来...
ClientArea.IsMouseDirectlyOverChanged += ClientArea_IsMouseDirectlyOverChanged;
其工作方式与“笨拙”的方式完全相同,但更加简单和整洁……无需走树,并且基于模板也不易碎。
有时候,我们的程序员对我们要走到正确的左边数感到惊讶。今晚我在做甜甜圈!
请注意,即使像我在这里一样透明,也请确保为边框提供背景,因为如果它为null,则鼠标实际上不在其上方,因此您不会得到任何鼠标事件。
希望这会有所帮助!