Loading a WPF Window without showing it
Asked Answered
S

12

59

I create a global hot key to show a window by PInvoking RegisterHotKey(). But to do this I need that window's HWND, which doesn't exist until the window is loaded, that means shown for the first time. But I don't want to show the window before I can set the hot key. Is there a way to create a HWND for that window that is invisible to the user?

Swearingen answered 9/9, 2009 at 11:12 Comment(0)
M
76

If you are targeting .NET 4.0 you can make use of the new EnsureHandle method available on the WindowInteropHelper:

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

(thanks to Thomas Levesque for pointing this out.)

If you are targeting an older version of the .NET Framework, the easiest way is to show the window to get to the HWND while setting a few properties to make sure that the window is invisible and doesn't steal focus:

var window = new Window() //make sure the window is invisible
{
    Width = 0,
    Height = 0,
    WindowStyle = WindowStyle.None,
    ShowInTaskbar = false,
    ShowActivated = false
};
window.Show();

Once you want to show the actual window you can then set the Content, the size and change the style back to a normal window.

Mariammarian answered 17/9, 2009 at 0:44 Comment(8)
Yes, this works, thanks. Setting WindowState even isn't necessary. Also, I set the content of the Window in XAML, but that's not important. Another thing is that WindowStartupLocation=CenterScreen doesn't work correctly this way, but that's easy to fix.Swearingen
removed the WindowState setter... thanks for letting me know.Mariammarian
I'd add ResizeMode = ResizeMode.NoResize too, because it removes the window border for resizing.Prioress
Don't foget to set MinWidth/Height if you have this set.Pharyngoscope
Might also want to add "Visibility = Visibility.Hidden" (taken from https://mcmap.net/q/330960/-completely-hide-wpf-window-on-startup)Maryalice
Also, do we have to call window.show() before trying to get its handle?Maryalice
Not sure what this does, but the Window.Loaded event is not triggered with this until the window is actually shown with Show(). So it has no effect for me.Barbarossa
@Barbarossa Had the same issue, the solution with EnsureHandle doesn't trigger the Loaded event, but the solution for older .NET versions does.Thom
G
21

You can also change the window into a so called message-only window. As this window type does not support graphical elements it will never be shown. Basically it comes down to calling:

    SetParent(hwnd, (IntPtr)HWND_MESSAGE);

Either create a dedicated message window which will always be hidden, or use the real GUI window and change it back to a normal window when you want to display it. See the code below for a more complete example.

    [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();
    }

For me the solution of setting the width, height to zero and style to none didn't work out, as it still showed a tiny window, with an annoying shadow of what seems to be the border around a 0x0 window (tested on Windows 7). Therefore I'm providing this alternative option.

Galvez answered 4/4, 2010 at 20:50 Comment(4)
This seems to be the only 100% solution.Lowtension
Thanks for this great tip. It really helped me, because all other solutions here caused some side-effects which weren't pretty. But your's does as well, sadly. I got a MetroWindow (using Fluent Ribbon Suite). Afterwards the window has a typical window border, which normally isn't visible for those MetroWindows... Any idea how to solve this?Incorporated
Perfect. It just needs: ShowActivated = false; after Visibility, because without it it flashes.Chalky
As @Incorporated mentions, this seems to change the window style. It looks like a Win 95 window.Isadora
A
18

This is a dirty hack, but it should work, and doesn't have the downsides of changing the opacity :

  • set the WindowStartupLocation to Manual
  • set the Top and Left properties to somewhere outside the screen
  • set ShowInTaskbar to false so that the user doesn't realize there is a new window
  • Show and Hide the window

You should now be able to retrieve the HWND

EDIT: another option, probably better : set ShowInTaskBar to false and WindowState to Minimized, then show it : it won't be visible at all

Anlage answered 12/9, 2009 at 16:32 Comment(4)
With your another option, I can see the window minimized in the lower left corner of the screen. But the first one looks promising.Swearingen
@svick: which OS are you using ? On Windows 7 the minimized window is not visibleAnlage
Ah, yes, I remember this behavior... it seems to have changed in Win7 (or maybe Vista)Anlage
The minimized window is still shortly visible in the lower left corner of the screen...Lebrun
A
12

I had already posted an answer to that question, but I just found a better solution.

If you just need to make sure that the HWND is created, without actually showing the window, you can do this:

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

(actually the EnsureHandle method wasn't available when the question was posted, it was introduced in .NET 4.0)

Anlage answered 4/6, 2013 at 13:15 Comment(3)
this probably should be the accepted answer now, or should I update my answer to include this as well? not sure what the respected practice is for framework version differences.Mariammarian
@PatrickKlug, I don't know either... You can either include this in your answer, or just reference my answer, whichever you think is best.Anlage
Can you please tell me how to invoke or this functionality? I need to call [Window w = new Window()] for initializing the object, but in this line itself it shows the window, before even calling the w.Show() !Melodics
M
5

I've never tried to do what you are doing, but if you need to show the Window to get the HWND, but don't want to show it, set the Window Opacity to 0. This will also prevent any hit testing from occurring. Then you could have a public method on the Window to change the Opacity to 100 when you want to make it visible.

Malathion answered 9/9, 2009 at 14:52 Comment(1)
Ufortunately, for the Opacity setting to be effective, AllowsTransparency must be set to true and this in turn forces WindowStyle to WindowStyle.None, which isn't what I want. Also, AllowsTransparency cannot be changed after the Window is shown, so I can't set it back afterwards.Swearingen
P
3

I know absolutely nothing about WPF, but could you create a message only window using other means (PInvoke for example) to receive the WM_HOTKEY message? If yes, then once you receive the WM_HOTKEY, you could launch the WPF window from there.

Pipistrelle answered 18/9, 2009 at 12:3 Comment(1)
+1, you can just use a Winforms Window on another thread if you want to do this.Leilaleilah
D
1

I've noticed that the last thing that happens when the window is being initialized, is the change of WindowState, if it differs from normal. So, you can actually make use of it:

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();
    }
}

That works fair enough for me. And it does not require working with any handles and stuff, and, more importantly, does not require to have a custom class for a window. Which is great for dynamically loaded XAML. And it is also a great way if you are making a fullscreen app. You do not even need to change its state back to normal or set proper width and height. Just go with

protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
    if (isStateChangeFirst) {
        isStateChangeFirst = false;

        window.ShowInTaskbar = true;
        window.Opacity = 1;
        window.Activate();
    }
}

And you're done.

And even if I am wrong in my assumption that change of state is last thing done when window is being loaded, you can still change to any other event, it does not really matter.

Detach answered 30/5, 2018 at 15:38 Comment(0)
F
0

The WindowInteropHelper class should allow you to get the HWND for the WPF window.

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

IntPtr hwnd = helper.Handle;

MSDN Documentation

Flori answered 9/9, 2009 at 11:47 Comment(1)
That's what I'm doing, but this way, the Window doesn't have a HWND yet, so helper.Handle is 0, which isn't what I need.Swearingen
C
0

Another option in a similar vein to setting the opacity to 0, is to set the size to 0 and set the position to be off the screen. This won't require the AllowsTransparency = True.

Also remember that once you have shown it once you can then hide it and still get the hwnd.

Capitulum answered 12/9, 2009 at 15:40 Comment(0)
B
0

Make the size of the window 0 x 0 px, put ShowInTaskBar to false, show it, then resize it when needed.

Batholith answered 18/9, 2009 at 11:11 Comment(0)
H
0

I've created extension method for showing invisible window, next Show calls will behave OK.

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;
    }
}
Hullda answered 7/6, 2017 at 7:43 Comment(0)
M
0

Start Wpf Window in Hidden mode:

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

Start Wpf Window in Visible mode:

WpfWindow w = new WpfWindow();
w.Show();
Melodics answered 22/10, 2019 at 16:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.