加载WPF窗口而不显示它

时间:2009-09-09 11:12:58

标签: wpf window hwnd

我通过PInvoking RegisterHotKey()创建一个全局热键来显示一个窗口。但要做到这一点,我需要窗口HWND,它在加载窗口之前不存在,这意味着第一次显示。但我不想在设置热键之前显示窗口。有没有办法为该窗口创建一个对用户不可见的HWND

12 个答案:

答案 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
  • TopLeft属性设置为屏幕外的某个位置
  • ShowInTaskbar设置为false,以便用户没有意识到有新窗口
  • ShowHide窗口

您现在应该能够检索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;

MSDN Documentation

答案 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();