我创建了一个全局热键来通过 PInvoking 显示一个窗口RegisterHotKey()
。但要做到这一点,我需要那个窗口HWND
,它在窗口加载之前不存在,这意味着第一次显示。但我不想在设置热键之前显示窗口。有没有办法为HWND
用户不可见的窗口创建一个?
12 回答
如果您的目标是 .NET 4.0,您可以使用以下EnsureHandle
可用的新方法WindowInteropHelper
:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(感谢 Thomas Levesque指出这一点。)
如果您的目标是 .NET Framework 的旧版本,最简单的方法是显示窗口以访问 HWND,同时设置一些属性以确保窗口不可见并且不会窃取焦点:
var window = new Window() //make sure the window is invisible
{
Width = 0,
Height = 0,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false
};
window.Show();
一旦您想显示实际窗口,您就可以设置内容、大小并将样式更改回普通窗口。
您还可以将窗口更改为所谓的仅消息窗口。由于此窗口类型不支持图形元素,因此永远不会显示。基本上它归结为调用:
SetParent(hwnd, (IntPtr)HWND_MESSAGE);
要么创建一个始终隐藏的专用消息窗口,要么使用真正的 GUI 窗口并在您想要显示它时将其更改回普通窗口。有关更完整的示例,请参见下面的代码。
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent);
private const int HWND_MESSAGE = -3;
private IntPtr hwnd;
private IntPtr oldParent;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwnd = hwndSource.Handle;
oldParent = SetParent(hwnd, (IntPtr)HWND_MESSAGE);
Visibility = Visibility.Hidden;
}
}
private void OpenWindowMenuItem_Click(object sender, RoutedEventArgs e)
{
SetParent(hwnd, oldParent);
Show();
Activate();
}
对我来说,将宽度、高度设置为零和样式设置为无的解决方案没有奏效,因为它仍然显示一个小窗口,带有一个恼人的阴影,似乎是 0x0 窗口周围的边框(在 Windows 7 上测试)。因此,我提供了这个替代选项。
这是一个肮脏的黑客,但它应该可以工作,并且没有改变不透明度的缺点:
- 设置
WindowStartupLocation
为Manual
- 将
Top
andLeft
属性设置到屏幕外的某个地方 - 设置
ShowInTaskbar
为 false 以便用户不会意识到有一个新窗口 Show
和Hide
窗户
您现在应该能够检索 HWND
编辑:另一个选项,可能更好:设置ShowInTaskBar
为 false 和WindowState
to Minimized
,然后显示它:它根本不可见
我已经发布了该问题的答案,但我刚刚找到了更好的解决方案。
如果您只需要确保创建了 HWND,而不实际显示窗口,则可以执行以下操作:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(实际上EnsureHandle
问题发布时该方法不可用,它是在.NET 4.0中引入的)
我从未尝试过您正在做的事情,但是如果您需要显示窗口以获取 HWND,但不想显示它,请将窗口不透明度设置为 0。这也将防止发生任何命中测试. 然后,当您想让它可见时,您可以在 Window 上使用公共方法将 Opacity 更改为 100。
我对 WPF 一无所知,但是您能否使用其他方式(例如 PInvoke)创建一个仅消息窗口来接收 WM_HOTKEY 消息?如果是,那么一旦您收到 WM_HOTKEY,您就可以从那里启动 WPF 窗口。
我注意到初始化窗口时发生的最后一件事是 的变化WindowState
,如果它与正常不同。因此,您实际上可以使用它:
public void InitializeWindow(Window window) {
window.Top = Int32.MinValue;
window.Left = Int32.MinValue;
window.Width = 0;
window.Height = 0;
window.ShowActivated = false;
window.ShowInTaskbar = false;
window.Opacity = 0;
window.StateChanged += OnBackgroundStateChanged;
window.WindowStyle = WindowStyle.None;
}
public void ShowWindow(Window window) {
window.Show();
window.WindowState = WindowState.Maximized;
}
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.Top = 300;
window.Left = 200;
window.Width = 760;
window.Height = 400;
window.WindowState = WindowState.Normal;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
这对我来说足够公平。它不需要使用任何句柄和东西,更重要的是,不需要为窗口提供自定义类。这对于动态加载的 XAML 非常有用。如果您正在制作全屏应用程序,这也是一个很好的方法。您甚至不需要将其状态更改回正常状态或设置适当的宽度和高度。一起去吧
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
你完成了。
即使我错误地假设状态更改是加载窗口时完成的最后一件事,您仍然可以更改为任何其他事件,这并不重要。
以隐藏模式启动 Wpf 窗口:
WpfWindow w = new WpfWindow() { Visibility = Visibility.Hidden };
以可见模式启动 Wpf 窗口:
WpfWindow w = new WpfWindow();
w.Show();
使窗口的大小为 0 x 0 px,将 ShowInTaskBar 设置为 false,显示它,然后在需要时调整它的大小。
WindowInteropHelper 类应该允许您获取 WPF 窗口的 HWND。
MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);
IntPtr hwnd = helper.Handle;
与将不透明度设置为 0 类似的另一个选项是将大小设置为 0 并将位置设置为离开屏幕。这不需要 AllowsTransparency = True。
还请记住,一旦您显示它一次,您就可以隐藏它并仍然获得 hwnd。
我已经创建了用于显示不可见窗口的扩展方法,下一个Show
调用将表现良好。
public static class WindowHelper
{
public static void ShowInvisible(this Window window)
{
// saving original settings
bool needToShowInTaskbar = window.ShowInTaskbar;
WindowState initialWindowState = window.WindowState;
// making window invisible
window.ShowInTaskbar = false;
window.WindowState = WindowState.Minimized;
// showing and hiding window
window.Show();
window.Hide();
// restoring original settings
window.ShowInTaskbar = needToShowInTaskbar;
window.WindowState = initialWindowState;
}
}