57

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

4

12 回答 12

72

如果您的目标是 .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();

一旦您想显示实际窗口,您就可以设置内容、大小并将样式更改回普通窗口。

于 2009-09-17T00:44:28.020 回答
21

您还可以将窗口更改为所谓的仅消息窗口。由于此窗口类型不支持图形元素,因此永远不会显示。基本上它归结为调用:

    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 上测试)。因此,我提供了这个替代选项。

于 2010-04-04T20:50:42.707 回答
18

这是一个肮脏的黑客,但它应该可以工作,并且没有改变不透明度的缺点:

  • 设置WindowStartupLocationManual
  • TopandLeft属性设置到屏幕外的某个地方
  • 设置ShowInTaskbar为 false 以便用户不会意识到有一个新窗口
  • ShowHide窗户

您现在应该能够检索 HWND

编辑:另一个选项,可能更好:设置ShowInTaskBar为 false 和WindowStateto Minimized,然后显示它:它根本不可见

于 2009-09-12T16:32:31.020 回答
11

我已经发布了该问题的答案,但我刚刚找到了更好的解决方案。

如果您只需要确保创建了 HWND,而不实际显示窗口,则可以执行以下操作:

    public void InitHwnd()
    {
        var helper = new WindowInteropHelper(this);
        helper.EnsureHandle();
    }

(实际上EnsureHandle问题发布时该方法不可用,它是在.NET 4.0中引入的)

于 2013-06-04T13:15:17.970 回答
5

我从未尝试过您正在做的事情,但是如果您需要显示窗口以获取 HWND,但不想显示它,请将窗口不透明度设置为 0。这也将防止发生任何命中测试. 然后,当您想让它可见时,您可以在 Window 上使用公共方法将 Opacity 更改为 100。

于 2009-09-09T14:52:39.587 回答
3

我对 WPF 一无所知,但是您能否使用其他方式(例如 PInvoke)创建一个仅消息窗口来接收 WM_HOTKEY 消息?如果是,那么一旦您收到 WM_HOTKEY,您就可以从那里启动 WPF 窗口。

于 2009-09-18T12:03:56.683 回答
1

我注意到初始化窗口时发生的最后一件事是 的变化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();
    }
}

你完成了。

即使我错误地假设状态更改是加载窗口时完成的最后一件事,您仍然可以更改为任何其他事件,这并不重要。

于 2018-05-30T15:38:58.377 回答
1

以隐藏模式启动 Wpf 窗口:

WpfWindow w = new WpfWindow() { Visibility = Visibility.Hidden };

以可见模式启动 Wpf 窗口:

WpfWindow w = new WpfWindow();
w.Show();
于 2019-10-22T16:38:30.680 回答
0

使窗口的大小为 0 x 0 px,将 ShowInTaskBar 设置为 false,显示它,然后在需要时调整它的大小。

于 2009-09-18T11:11:22.613 回答
0

WindowInteropHelper 类应该允许您获取 WPF 窗口的 HWND。

MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);

IntPtr hwnd = helper.Handle;

MSDN 文档

于 2009-09-09T11:47:07.503 回答
0

与将不透明度设置为 0 类似的另一个选项是将大小设置为 0 并将位置设置为离开屏幕。这不需要 AllowsTransparency = True。

还请记住,一旦您显示它一次,您就可以隐藏它并仍然获得 hwnd。

于 2009-09-12T15:40:48.967 回答
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;
    }
}
于 2017-06-07T07:43:27.070 回答