代码之家  ›  专栏  ›  技术社区  ›  svick Raja Nadar

加载未显示的wpf窗口

  •  49
  • svick Raja Nadar  · 技术社区  · 16 年前

    我创建了一个全局热键,通过pinvoking显示窗口 RegisterHotKey() . 但要做到这一点我需要那扇窗户 HWND ,在加载窗口之前不存在,这意味着第一次显示。但我不想在我设定热键之前先展示一下窗口。有没有办法创造一个 HWND 对于用户不可见的窗口?

    11 回复  |  直到 8 年前
        1
  •  61
  •   Community Mohan Dere    9 年前

    如果你正在瞄准.NET 4,你可以使用新的 EnsureHandle 方法可用于 WindowInteropHelper :

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

    (感谢托马斯·莱维斯克 pointing this out. )

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

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

        2
  •  17
  •   DJP    16 年前

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

        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上测试)周围似乎有一个恼人的阴影。所以我提供了另一种选择。

        3
  •  16
  •   Thomas Levesque    16 年前

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

    • 设置 WindowStartupLocation Manual
    • 设置 Top Left 属性到屏幕外的某个位置
    • 设置 ShowInTaskbar 设置为false,这样用户就不会意识到有一个新窗口
    • Show Hide 窗户

    你现在应该可以检索hwnd了

    编辑:另一个选项,可能更好:set ShowInTaskBar 假和 WindowState Minimized ,然后显示出来:根本看不到

        4
  •  10
  •   Thomas Levesque    13 年前

    我已经发布了这个问题的答案,但我只是找到了一个更好的解决方案。

    如果您只需要确保创建HWND,而不实际显示窗口,您可以这样做:

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

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

        5
  •  5
  •   Joel Cochran    16 年前

    我从来没有试过做你正在做的事,但是如果你需要打开窗户去拿公路,但是不想 显示 设置窗口不透明度为0。这也将防止任何命中测试的发生。然后你可以在窗口上有一个公共方法,当你想使其可见时,将不透明度更改为100。

        6
  •  3
  •   Agnel Kurian    16 年前

    我对wpf一无所知,但你能不能创造一个 message only window 使用其他方式(例如pinvoke)接收wm_热键消息?如果是,那么一旦收到wm_热键,就可以从那里启动wpf窗口。

        7
  •  0
  •   Shannon Cornish    16 年前

    windowinterophelper类应该允许您获取wpf窗口的hwnd。

    MyWindow win = new MyWindow();
    WindowInteropHelper helper = new WindowInteropHelper(win);
    
    IntPtr hwnd = helper.Handle;
    

    MSDN Documentation

        8
  •  0
  •   Ben Childs    16 年前

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

    还记得,一旦你已经显示了一次,你就可以隐藏它,仍然得到HWND。

        9
  •  0
  •   luvieere    16 年前

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

        10
  •  0
  •   Vadim Ovchinnikov    9 年前

    我已经创建了显示不可见窗口的扩展方法,下一步 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;
        }
    }
    
        11
  •  0
  •   Nullcaller    8 年前

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

    你完了。

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