将鼠标移到窗口或控件上方时,为什么不触发“ IsMouseDirectlyOverChanged”?

时间:2018-08-24 05:40:42

标签: c# wpf

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;

它现在会触发,但是只有当我实际单击该按钮时才会触发,而当我将鼠标悬停在该按钮上时不会触发。

那么当我将鼠标悬停在客户区域上方时,如何检测?

2 个答案:

答案 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。这是有道理的,因为BorderWindow的默认模板的一部分。

接下来,我打开了Snoop,看了一眼视觉树,果然在WindowButton之间是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,则鼠标实际上不在其上方,因此您不会得到任何鼠标事件。

希望这会有所帮助!