簡體   English   中英

為什么將鼠標移到窗口或控件上時不會觸發“IsMouseDirectlyOverChanged”?

[英]Why doesn't 'IsMouseDirectlyOverChanged' fire when moving the mouse over a Window or Control?

有點摸不着頭腦。 我試圖檢測用戶的鼠標何時位於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;

它現在會觸發,但只有當我實際點擊按鈕時才會觸發,而不是當我將鼠標懸停在它上面時。

那么如何檢測我何時直接懸停在客戶區上?

來自 MSDN 的UIElement.IsMouseDirectlyOver 屬性

備注:與 IsMouseOver 不同,此屬性僅在鼠標指針懸停在文字元素上時才為真 - 就像命中測試一樣。 如果鼠標指針位於子元素上方,特別是位於元素更深層模板和合成中的元素上方,則此屬性將為 false。

我認為這解釋了為什么它不會在窗口上觸發,我無法解釋為什么當您單擊按鈕時(而不是懸停在它上面時)它會觸發。

MouseEnter / MouseLeave事件和IsMouseOver屬性如何?

好的,我想通了。 它不像人們預期的那樣工作,因為鼠標響應的是視覺樹,而不是邏輯樹。

初次嘗試

我通過首先為Window連接一個MouseMove事件(在邏輯樹上工作)來發現這一點,並在處理程序中,我添加了以下內容:

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

在控制台中,我看到它實際上是在一個Border控件上,而不是我所期望的Window 這是有道理的,因為BorderWindow的默認模板的一部分。

我接下來打開 Snoop 並查看可視化樹,果然,在WindowButton是那個Border (以及其他一些東西)。好吧,簡單……我將從Button走可視化樹到Border

但是...在Window的構造函數期間, Button還沒有可視化父級,因為Window的模板尚未加載! (出於某種原因,即使在調用base.OnApplyTemplate之后,您也無法在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永遠不會改變的模板。 不好!

然后我有一個令人頭疼的時刻。 答案太簡單了,我幾乎尷尬了:

<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>

是的,就像添加我自己的邊框一樣簡單,然后我像這樣連接......

ClientArea.IsMouseDirectlyOverChanged += ClientArea_IsMouseDirectlyOverChanged;

這與“笨拙”的方式完全相同,但更簡單、更干凈……不走樹,並且基於模板不脆弱。

有時我們程序員會驚訝於我們向右走多少左。 今晚我在做甜甜圈!

重要提示:設置背景!

確保為您的邊框設置背景,即使只是像我在這里設置的那樣設置為Transparent 這是因為如果背景為空,則它不存在,因此鼠標不會“懸停”,因此您不會收到任何鼠標事件。 將其設置為Transparent可以簡單地解決並且根本不改變視覺效果。

希望這可以幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM