tags:

views:

1165

answers:

9

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?

A: 

Windows in WPF are created prior to being shown.

Try the following:

System.Windows.Window myWindow = new MyWindow(); // This will initialize, but not show the window
myWindow.Show(); // This shows the window, and makes it visible

You should be able to create the window, and show it later, as you wish.

Reed Copsey
But just calling the constructor does't create the HWND I need. And Show() creates the HWND but also makes the window visible and I don't want that.
svick
A: 

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

Shannon Cornish
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.
svick
+5  A: 

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.

Joel Cochran
This is sort of a dirty hack, but it will work for sure.
Anderson Imes
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.
svick
A: 

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.

Ben Childs
+6  A: 

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

Thomas Levesque
+1 ShowInTaskBar = false and WindowState = Minimized works.
Mehmet Aras
With your another option, I can see the window minimized in the lower left corner of the screen. But the first one looks promising.
svick
@svick: which OS are you using ? On Windows 7 the minimized window is not visible
Thomas Levesque
I use Windows XP
svick
Ah, yes, I remember this behavior... it seems to have changed in Win7 (or maybe Vista)
Thomas Levesque
+4  A: 

I think you will need to show the window to get to the HWND... you could simply set the Size of the Window to 0 and change a few other properties to make it invisible:

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 could set the Content and change the style back to a normal window.

Patrick Klug
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.
svick
removed the WindowState setter... thanks for letting me know.
Patrick Klug
A: 

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

luvieere
+3  A: 

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.

Vulcan Eager
+1, you can just use a Winforms Window on another thread if you want to do this.
Paul Betts
+1  A: 

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.

DJP