【问题标题】:How can I make an "overlay" window that allows mouse clicks to pass through to windows below it, but still allow some controls to be clicked?如何制作一个“覆盖”窗口,允许鼠标点击通过它下面的窗口,但仍然允许点击一些控件?
【发布时间】:2019-11-13 05:39:31
【问题描述】:

我正在尝试在屏幕上的所有其他内容之上创建一个充当“叠加层”的窗口。它不应该干扰到其他窗口的输入,所以我应该能够点击它下面的任何内容,但有一些例外,比如我可以单击覆盖层上的小按钮来“关闭”覆盖层上的其他 UI 元素。

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

  1. 在窗口上设置Topmost="True"。这没有问题。这正是我想要的,它使我的窗口始终绘制在其他窗口之上。
  2. 将窗口背景设为{x:Null},并在窗口上添加AllowsTransparency="True"WindowStyle="None"。这允许点击允许点击透明部分,并允许点击可见的按钮,但是,由于点击只适用于不可见的东西,这不是很有用。您知道,永远不会在叠加层上真正显示任何信息或破坏点击行为。
  3. 执行上述操作,同时在我希望能够点击的元素上设置IsHitTestVisible="False"。这禁用了使用鼠标与控件进行交互,但它不允许点击传递到它下方的窗口。

  4. 使用来自this accepted answerSetWindowLong 方法。这样做的问题是它使 整个 窗口点击通过。您无法再使叠加层上的某些按钮可点击。

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

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

【问题讨论】:

  • 你可以尝试将窗口背景设置为透明而不是 null 并尝试它是否有效。它通常适用于相互叠加的不同控件。检查它是否也适合 Windows..
  • 我尝试了透明而不是null,它具有完全相同的效果。这使得 ui 的可见部分消耗鼠标点击,而不是让点击传递到下面的窗口。
  • 我认为这只能通过windows句柄来实现

标签: wpf wpf-controls


【解决方案1】:

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

代码

this answer 复制的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>
  • Topmost="True" 导致此窗口始终绘制在顶部,即使 当其他窗口有焦点时。
  • WindowStyle="None" 删除 窗口标题栏和边框AllowsTransparency="True" 做什么 顾名思义,它允许窗口是透明的。
  • Background="{x:Null}" 使其透明。你也可以设置 到具有 0 OpacitySolidColorBrush。无论哪种方式都有效。

窗口代码隐藏:

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);
}

【讨论】:

    猜你喜欢
    • 2016-09-07
    • 2011-11-22
    • 2015-09-27
    • 2017-05-16
    • 1970-01-01
    • 1970-01-01
    • 2020-02-26
    • 2017-08-19
    • 1970-01-01
    相关资源
    最近更新 更多