我通过PInvoking RegisterHotKey()
创建一个全局热键来显示一个窗口。但要做到这一点,我需要窗口HWND
,它在加载窗口之前不存在,这意味着第一次显示。但我不想在设置热键之前显示窗口。有没有办法为该窗口创建一个对用户不可见的HWND
?
答案 0 :(得分:64)
如果您的目标是.NET 4.0,则可以使用EnsureHandle
上提供的新WindowInteropHelper
方法:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(感谢Thomas Levesque pointing this out.)
如果您的目标是.NET Framework的旧版本,最简单的方法是显示窗口以获取HWND,同时设置一些属性以确保窗口不可见且不会窃取焦点:< / p>
var window = new Window() //make sure the window is invisible
{
Width = 0,
Height = 0,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false
};
window.Show();
一旦您想要显示实际窗口,您就可以设置内容,大小并将样式更改回正常窗口。
答案 1 :(得分:17)
您还可以将窗口更改为所谓的仅消息窗口。由于此窗口类型不支持图形元素,因此永远不会显示。基本上它归结为:
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上)。因此,我提供了这种替代选择。
答案 2 :(得分:16)
这是一个肮脏的黑客,但它应该工作,并没有改变不透明度的缺点:
WindowStartupLocation
设置为Manual
Top
和Left
属性设置为屏幕外的某个位置ShowInTaskbar
设置为false,以便用户没有意识到有新窗口Show
和Hide
窗口您现在应该能够检索HWND
编辑:另一种选择,可能更好:将ShowInTaskBar
设置为false,将WindowState
设置为Minimized
,然后显示:它根本不可见
答案 3 :(得分:10)
我已经发布了这个问题的答案,但我找到了一个更好的解决方案。
如果您只需要确保创建HWND,而不实际显示窗口,则可以执行以下操作:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(实际上EnsureHandle
方法在发布问题时不可用,它是在.NET 4.0中引入的)
答案 4 :(得分:5)
我从未尝试过你正在做的事情,但如果你需要显示Window来获得HWND,但又不想显示它,请将Window Opacity设置为0这也可以防止任何命中测试的发生。然后,您可以在窗口上使用公共方法将“不透明度”更改为100,以使其可见。
答案 5 :(得分:3)
我对WPF一无所知,但您是否可以使用其他方法(例如PInvoke)创建message only window来接收WM_HOTKEY消息?如果是,那么一旦收到WM_HOTKEY,就可以从那里启动WPF窗口。
答案 6 :(得分:0)
WindowInteropHelper类应该允许你获取WPF窗口的HWND。
MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);
IntPtr hwnd = helper.Handle;
答案 7 :(得分:0)
将不透明度设置为0的另一个选择是将大小设置为0并将位置设置为离开屏幕。这不需要AllowTransparency = True。
还要记住,一旦你展示了它,你就可以隐藏它并仍然得到hwnd。
答案 8 :(得分:0)
使窗口的大小为0 x 0 px,将ShowInTaskBar设置为false,显示它,然后在需要时调整大小。
答案 9 :(得分:0)
我创建了用于显示隐藏窗口的扩展方法,下一个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;
}
}
答案 10 :(得分:0)
我注意到窗口初始化时发生的最后一件事是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();
}
}
你已经完成了。
即使我错误地认为状态的改变是在加载窗口时最后完成的事情,你仍然可以改变到任何其他事件,这并不重要。
答案 11 :(得分:0)
以隐藏模式启动Wpf窗口:
WpfWindow w = new WpfWindow() { Visibility = Visibility.Hidden };
//w.Show();
以可见模式启动Wpf窗口:
WpfWindow w = new WpfWindow();
w.Show();