加载WPF窗口而不显示它
我通过PInvoking加载WPF窗口而不显示它,wpf,window,hwnd,Wpf,Window,Hwnd,我通过PInvokingRegisterHotKey()创建一个全局热键来显示窗口。但要做到这一点,我需要窗口的HWND,它在加载窗口之前不存在,这意味着第一次显示。但我不想在设置热键之前显示窗口。是否有方法为用户不可见的窗口创建HWND?WindowInteropHelper类应允许您获取WPF窗口的HWND MyWindow win = new MyWindow(); WindowInteropHelper helper = new WindowInteropHelper(win); In
RegisterHotKey()
创建一个全局热键来显示窗口。但要做到这一点,我需要窗口的HWND
,它在加载窗口之前不存在,这意味着第一次显示。但我不想在设置热键之前显示窗口。是否有方法为用户不可见的窗口创建HWND
?WindowInteropHelper类应允许您获取WPF窗口的HWND
MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);
IntPtr hwnd = helper.Handle;
我从未尝试过执行您正在执行的操作,但是如果您需要显示窗口以获取HWND,但不想显示,请将窗口不透明度设置为0。这也将防止发生任何命中测试。然后你可以在窗口上有一个公共方法,当你想要使不透明度可见时,将其更改为100。另一个类似于将不透明度设置为0的方法是将大小设置为0,并将位置设置为远离屏幕。这不需要AllowsTransparency=True
还要记住,一旦你展示了它,你就可以隐藏它,仍然可以得到hwnd。这是一个肮脏的破解,但它应该可以工作,并且没有改变不透明度的缺点:
- 将
设置为WindowStartupLocation
Manual
- 将
和顶部
属性设置为屏幕外的某个位置左侧
- 将
设置为false,这样用户就不会意识到有一个新窗口ShowInTaskbar
和显示
窗口隐藏
编辑:另一个选项,可能更好:将
ShowInTaskBar
设置为false,将WindowState
设置为Minimized
,然后显示它:如果您的目标是.NET 4.0,它将根本不可见。您可以使用WindowInteropHelper
上提供的新的EnsureHandle
方法:
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();
一旦你想显示实际窗口,你就可以设置内容、大小并将样式改回普通窗口。将窗口的大小设为0 x 0 px,将ShowInTaskBar设为false,显示它,然后在需要时调整它的大小。我对WPF一无所知,但你能用其他方法(例如PInvoke)创建一个新窗口吗要接收WM_热键消息?如果是,那么一旦您收到WM_热键,您就可以从那里启动WPF窗口。您还可以将窗口更改为所谓的仅消息窗口。由于此窗口类型不支持图形元素,因此将永远不会显示它。基本上可以归结为:
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窗口周围有一个恼人的阴影(在Windows7上测试)。因此,我提供了这个备选方案。我已经发布了这个问题的答案,但我刚刚找到了一个更好的解决方案 如果只需要确保创建了HWND,而不实际显示窗口,则可以执行以下操作:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(实际上,
EnsureHandle
方法在发布问题时不可用,它是在.NET 4.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;
}
}
我注意到,初始化窗口时发生的最后一件事是更改
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窗口:
在可见模式下启动Wpf窗口:
这就是我正在做的,但是这样,窗口还没有HWND,所以helper.Handle是0,这不是我需要的。很奇怪,要使不透明度设置有效,AllowTransparency必须设置为true,这反过来会将WindowsStyle强制为WindowsStyle。无,这不是我想要的。此外,AllowTransparency在显示窗口后无法更改,因此我无法在显示后将其设置回原位。使用另一个选项,我可以在屏幕左下角看到最小化的窗口。但是第一个看起来很有希望。@svick:你用的是哪个操作系统?在Windows7上最小化的窗口不可见啊,是的,我记得这个行为。。。在Win7(或者Vista)中它似乎已经改变了。是的,这很有效,谢谢。甚至不需要设置WindowsState。另外,我用XAML设置了窗口的内容,但这并不重要。另一件事是WindowsStartupLocation=CenterScreen无法以这种方式正常工作,但这很容易修复。已删除WindowsState设置程序。。。感谢您让我知道。我也要添加
ResizeMode=ResizeMode.NoResize
,因为它会删除窗口边框以调整大小。如果您有此设置,请不要忘记设置最小宽度/高度。可能还需要添加“可见性=可见性.隐藏”(取自
WpfWindow w = new WpfWindow() { Visibility = Visibility.Hidden };
WpfWindow w = new WpfWindow();
w.Show();