Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么';t';IsMouseDirectlyOverChanged';在窗口或控件上移动鼠标时触发?_C#_Wpf - Fatal编程技术网

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机器,我不能确定。我试过你的代码,它的行为和你描述的完全一样。好的。。。多恩!现在我明白了,这个答案很有道理。看看我刚刚发布的内容。事情太简单了,我几乎感到尴尬,因为我没有早点想到这一点我只是有另一个想法(我还没有机会检查)。我认为,当你点击事件时,它会在按钮上触发的原因是,当你点击它时,它会更改模板,并且在模板被交换出去的瞬间,鼠标可能直接位于可视树中按钮的根上(即按钮!)所以它开火了。这只是一个猜测,但确实有道理。