代码之家  ›  专栏  ›  技术社区  ›  Mark A. Donohoe

当鼠标移到窗口或控件上时,“IsmouseDirectlyOverChanged”为什么不开火?

  •  0
  • Mark A. Donohoe  · 技术社区  · 6 年前

    这有点让我头疼。我正在尝试检测用户的鼠标何时位于 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 回复  |  直到 6 年前
        1
  •  0
  •   kennyzx    6 年前

    UIElement.IsMouseDirectlyOver property 来自msdn。

    评论: 与IsMouseOver不同,只有当鼠标指针位于文本元素上时,此属性才为真,因为它用于命中测试。如果鼠标指针位于子元素上,特别是位于作为元素深层模板和合成一部分的元素上,则此属性将为假。

    我认为这可以解释为什么它不在窗口上触发,我不能解释为什么当你点击按钮时它会触发(而不是在它上面盘旋时)。

    怎么样 MouseEnter / MouseLeave 事件和 IsMouseOver 财产?

        2
  •  0
  •   Mark A. Donohoe    6 年前

    好吧,我知道了。它并不像人们期望的那样工作,因为鼠标对可视化树而不是逻辑树做出响应。

    我第一次发现这个是因为 MouseMove 事件(在逻辑树上工作) Window 在处理程序中,我添加了以下内容:

    Console.WriteLine($"The mouse is over '{System.Windows.Input.Mouse.DirectlyOver}'");
    

    在控制台里,我看到它实际上是在 Border 控制,而不是 窗口 如我所料。这是有道理的 边界 是默认模板的一部分 窗口

    接着我打开鼻涕,看了看那棵可见的树,果然,在 窗口 以及 Button 是吗? 边界 (除了其他一些事情)好的,简单的…我将从 纽扣 边界

    但是…在 窗口 的构造函数 纽扣 还没有可视父级,因为 窗口 的模板尚未加载!(由于某种原因,您无法在 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 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;
    

    这和“笨拙”的方式完全一样,但更简单更干净……不走在树上,也不易碎的基于模板。

    有时,我们的程序员会惊讶于我们需要多少个左撇子才能向右。今晚我在做甜甜圈!

    不过,有一个警告……一定要给你的边界一个背景,即使像我这里一样透明,因为如果它为空,鼠标实际上不在上面,所以你不会得到任何鼠标事件。

    希望这有帮助!