我怎样才能使 "overlay" window 允许鼠标点击传递到它下面的 windows,但仍然允许点击某些控件?
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?
我正在尝试制作一个 window 作为 "overlay" 在我屏幕上的所有其他内容之上。它不应该干扰其他 windows 的输入,所以我应该能够点击进入它下面的任何内容,除了一些例外,比如我可以点击覆盖上的小按钮 "dismiss" 其他UI 个叠加层上的元素。
我发现了什么,以及每个问题:
- 在 window 上设置
Topmost="True"
。这没有问题。这正是我想要的,它使我的 window 总是绘制在其他 windows. 之上
- 将 window 背景设为
{x:Null}
,并在 Window 上添加 AllowsTransparency="True"
和 WindowStyle="None"
。这允许点击允许点击透明部分,并允许点击可见的按钮,但是,由于点击只对不可见的东西有效,所以这不是很有用。您知道,实际上永远 显示 叠加层上的任何信息或破坏点击行为的信息。
执行上述操作,但还在我希望能够点击的元素上设置 IsHitTestVisible="False"
。这会禁用使用鼠标与控件进行交互,但它不允许点击传递到其下方的 window。
使用 this accepted answer 中的 SetWindowLong
方法。这样做的问题是它使 整个 window 点击。您无法再使叠加层上的某些按钮可点击。
- 使用上面的
SetWindowLong
方法,但将其传递给按钮的 hwnd
并使按钮 不 对点击透明。这是行不通的。实际上甚至不可能这样做,因为与 WinForms 不同,WPF 按钮不是 windows 并且它们没有 window 句柄。
我尝试将 window 的扩展样式切换为当鼠标悬停在按钮上时不为 "transparent" 并在鼠标离开按钮时再次使其透明,但这也不起作用,因为当它设置为透明时,它也不会拾取 MouseMove、MouseEnter 或 MouseLeave 事件。
经过更多的挖掘,我找到了答案。它结合了我在问题#4 和#5 中描述的内容,但必须以不同的方式进行。我读到 WPF 控件没有 window 句柄,因为 wpf 控件不像 winforms 控件那样 windows。这不完全正确。 WPF 控件确实有 window 个句柄。
密码
WindowsServices class 从 this answer 复制,使用额外的方法 删除 透明度:
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);
}
}
*注意:在此上下文中的透明度是指 window 对鼠标输入透明,而不是视觉上透明。您仍然可以看到 window 上呈现的任何内容,但鼠标点击将发送到 window 后面的任何内容。
Window 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"
导致此 window 始终绘制在顶部,即使
当其他 windows 获得焦点时。
WindowStyle="None"
删除了
window 标题栏和边框 AllowsTransparency="True"
做什么
顾名思义,它允许 window 透明。
Background="{x:Null}"
使其透明。你也可以设置它
到具有 0 Opacity
的 SolidColorBrush
。无论哪种方式都有效。
Window code-behind:
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);
}
我正在尝试制作一个 window 作为 "overlay" 在我屏幕上的所有其他内容之上。它不应该干扰其他 windows 的输入,所以我应该能够点击进入它下面的任何内容,除了一些例外,比如我可以点击覆盖上的小按钮 "dismiss" 其他UI 个叠加层上的元素。
我发现了什么,以及每个问题:
- 在 window 上设置
Topmost="True"
。这没有问题。这正是我想要的,它使我的 window 总是绘制在其他 windows. 之上
- 将 window 背景设为
{x:Null}
,并在 Window 上添加AllowsTransparency="True"
和WindowStyle="None"
。这允许点击允许点击透明部分,并允许点击可见的按钮,但是,由于点击只对不可见的东西有效,所以这不是很有用。您知道,实际上永远 显示 叠加层上的任何信息或破坏点击行为的信息。 执行上述操作,但还在我希望能够点击的元素上设置
IsHitTestVisible="False"
。这会禁用使用鼠标与控件进行交互,但它不允许点击传递到其下方的 window。使用 this accepted answer 中的
SetWindowLong
方法。这样做的问题是它使 整个 window 点击。您无法再使叠加层上的某些按钮可点击。- 使用上面的
SetWindowLong
方法,但将其传递给按钮的hwnd
并使按钮 不 对点击透明。这是行不通的。实际上甚至不可能这样做,因为与 WinForms 不同,WPF 按钮不是 windows 并且它们没有 window 句柄。
我尝试将 window 的扩展样式切换为当鼠标悬停在按钮上时不为 "transparent" 并在鼠标离开按钮时再次使其透明,但这也不起作用,因为当它设置为透明时,它也不会拾取 MouseMove、MouseEnter 或 MouseLeave 事件。
经过更多的挖掘,我找到了答案。它结合了我在问题#4 和#5 中描述的内容,但必须以不同的方式进行。我读到 WPF 控件没有 window 句柄,因为 wpf 控件不像 winforms 控件那样 windows。这不完全正确。 WPF 控件确实有 window 个句柄。
密码
WindowsServices class 从 this answer 复制,使用额外的方法 删除 透明度:
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);
}
}
*注意:在此上下文中的透明度是指 window 对鼠标输入透明,而不是视觉上透明。您仍然可以看到 window 上呈现的任何内容,但鼠标点击将发送到 window 后面的任何内容。
Window 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"
导致此 window 始终绘制在顶部,即使 当其他 windows 获得焦点时。WindowStyle="None"
删除了 window 标题栏和边框AllowsTransparency="True"
做什么 顾名思义,它允许 window 透明。Background="{x:Null}"
使其透明。你也可以设置它 到具有 0Opacity
的SolidColorBrush
。无论哪种方式都有效。
Window code-behind:
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);
}