Wpf 我怎样才能做出一个“回答”;“覆盖”;允许鼠标单击通过其下方的窗口,但仍允许单击某些控件的窗口?

Wpf 我怎样才能做出一个“回答”;“覆盖”;允许鼠标单击通过其下方的窗口,但仍允许单击某些控件的窗口?,wpf,wpf-controls,Wpf,Wpf Controls,我试图在我的屏幕上做一个窗口,作为一个“覆盖物”覆盖在所有的东西之上。它不应该干扰其他窗口的输入,所以我应该能够点击它下面的任何东西,除了一些例外,比如覆盖层上的小按钮,我可以点击它“关闭”覆盖层上的其他UI元素 我发现了什么,以及每个方面的问题: 在窗口上设置Topmost=“True”。这没有问题。这正是我想要的,它使我的窗口总是画在其他窗口的顶部 将窗口背景设置为{x:Null},并在窗口上添加allowTransparency=“True”和WindowStyle=“None”。这允许点

我试图在我的屏幕上做一个窗口,作为一个“覆盖物”覆盖在所有的东西之上。它不应该干扰其他窗口的输入,所以我应该能够点击它下面的任何东西,除了一些例外,比如覆盖层上的小按钮,我可以点击它“关闭”覆盖层上的其他UI元素

我发现了什么,以及每个方面的问题:

  • 在窗口上设置
    Topmost=“True”
    。这没有问题。这正是我想要的,它使我的窗口总是画在其他窗口的顶部
  • 将窗口背景设置为
    {x:Null}
    ,并在窗口上添加
    allowTransparency=“True”
    WindowStyle=“None”
    。这允许点击透明部分,并允许点击可见的按钮,但是,由于点击只对不可见的东西起作用,这不是很有用。你知道,永远不会在覆盖上显示任何信息,或者破坏点击行为
  • 执行上述操作,但还要在我希望能够单击的元素上设置
    ishitestvisible=“False”
    。这将禁用使用鼠标与控件的交互,但不允许单击通过鼠标下方的窗口

  • 使用中的
    SetWindowLong
    方法。这样做的问题是,它会使整个窗口点击。您不再能够使覆盖图上的某些按钮可单击

  • 使用上面的
    SetWindowLong
    方法,但将按钮的
    hwnd
    传递给它,并使按钮对单击不透明。这是行不通的。实际上甚至不可能这样做,因为与WinForms不同,WPF按钮不是窗口,并且没有窗口句柄

  • 我曾尝试在鼠标位于按钮上方时将窗口的扩展样式切换为非“透明”,并在鼠标离开按钮时使其再次透明,但这也不起作用,因为当它设置为透明时,它也不会拾取MouseMove、MouseEnter、,或者鼠标移动事件。

    经过进一步的挖掘,我找到了答案。它结合了我在问题4和5中描述的内容,但必须以不同的方式进行。我读到WPF控件没有窗口句柄,因为WPF控件不像winforms控件那样是窗口。那不完全是真的。WPF控件确实有窗口句柄

    代码

    从复制的WindowsServices类,使用额外的方法删除透明度:

    public static class WindowsServices
    {
      const int WS_EX_TRANSPARENT = 0x00000020;
      const int GWL_EXSTYLE = (-20);
    
      [DllImport("user32.dll")]
      static extern int GetWindowLong(IntPtr hwnd, int index);
    
      [DllImport("user32.dll")]
      static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
    
      public static void SetWindowExTransparent(IntPtr hwnd)
      {
        var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
        SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
      }
    
      public static void SetWindowExNotTransparent(IntPtr hwnd)
      {
        var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
        SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);
      }
    }
    
    *注意:此上下文中的透明度是指窗口对鼠标输入透明,而不是视觉透明。您仍然可以看到窗口上渲染的任何内容,但鼠标单击将发送到它后面的任何窗口

    窗口xaml:

    <Window x:Class="TransClickThrough.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="450" Width="800"
            Topmost="True"
            WindowStyle="None"
            AllowsTransparency="True"
            Background="{x:Null}">
        <Grid>
        <Button x:Name="button1" Content="Button" HorizontalAlignment="Left" Margin="183,136,0,0" VerticalAlignment="Top"/>
      </Grid>
    </Window>
    

    你能试着将窗口背景设置为透明而不是空的,然后试试看它是否有效。通常情况下,不同的控件重叠在一起时,它都能工作。检查它是否也适用于Windows。我尝试了透明而不是null,它的效果完全相同。这使得ui的可见部分使用鼠标点击,而不是让点击传递到下面的窗口。我认为这只能通过windows手柄实现
    protected override void OnSourceInitialized(EventArgs e)
    {
      base.OnSourceInitialized(e);
    
      // Make entire window and everything in it "transparent" to the Mouse
      var windowHwnd = new WindowInteropHelper(this).Handle;
      WindowsServices.SetWindowExTransparent(windowHwnd);
    
      // Make the button "visible" to the Mouse
      var buttonHwndSource = (HwndSource)HwndSource.FromVisual(btn);
      var buttonHwnd = buttonHwndSource.Handle;
      WindowsServices.SetWindowExNotTransparent(buttonHwnd);
    }