Best way to hide a window from the Alt-Tab program switcher?
Update:
According to @donovan, modern days WPF supports this natively, through setting
ShowInTaskbar="False"
and Visibility="Hidden"
in the XAML. (I haven't tested this yet, but nevertheless decided to bump the comment visibility)
Original answer:
There are two ways of hiding a window from the task switcher in Win32 API:
- to add the
WS_EX_TOOLWINDOW
extended window style - that's the right approach. - to make it a child window of another window.
Unfortunately, WPF does not support as flexible control over the window style as Win32, thus a window with WindowStyle=ToolWindow
ends up with the default WS_CAPTION
and WS_SYSMENU
styles, which causes it to have a caption and a close button. On the other hand, you can remove these two styles by setting WindowStyle=None
, however that will not set the WS_EX_TOOLWINDOW
extended style and the window will not be hidden from the task switcher.
To have a WPF window with WindowStyle=None
that is also hidden from the task switcher, one can either of two ways:
- go with the sample code above and make the window a child window of a small hidden tool window
- modify the window style to also include the
WS_EX_TOOLWINDOW
extended style.
I personally prefer the second approach. Then again, I do some advanced stuff like extending the glass in the client area and enabling WPF drawing in the caption anyway, so a little bit of interop is not a big problem.
Here's the sample code for the Win32 interop solution approach. First, the XAML part:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300"
ShowInTaskbar="False" WindowStyle="None"
Loaded="Window_Loaded" >
Nothing too fancy here, we just declare a window with WindowStyle=None
and ShowInTaskbar=False
. We also add a handler to the Loaded event where we will modify the extended window style. We can't do that work in the constructor, as there's no window handle at that point yet. The event handler itself is very simple:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
WindowInteropHelper wndHelper = new WindowInteropHelper(this);
int exStyle = (int)GetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE);
exStyle |= (int)ExtendedWindowStyles.WS_EX_TOOLWINDOW;
SetWindowLong(wndHelper.Handle, (int)GetWindowLongFields.GWL_EXSTYLE, (IntPtr)exStyle);
}
And the Win32 interop declarations. I've removed all unnecessary styles from the enums, just to keep the sample code here small. Also, unfortunately the SetWindowLongPtr
entry point is not found in user32.dll on Windows XP, hence the trick with routing the call through the SetWindowLong
instead.
#region Window styles
[Flags]
public enum ExtendedWindowStyles
{
// ...
WS_EX_TOOLWINDOW = 0x00000080,
// ...
}
public enum GetWindowLongFields
{
// ...
GWL_EXSTYLE = (-20),
// ...
}
[DllImport("user32.dll")]
public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);
public static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
{
int error = 0;
IntPtr result = IntPtr.Zero;
// Win32 SetWindowLong doesn't clear error on success
SetLastError(0);
if (IntPtr.Size == 4)
{
// use SetWindowLong
Int32 tempResult = IntSetWindowLong(hWnd, nIndex, IntPtrToInt32(dwNewLong));
error = Marshal.GetLastWin32Error();
result = new IntPtr(tempResult);
}
else
{
// use SetWindowLongPtr
result = IntSetWindowLongPtr(hWnd, nIndex, dwNewLong);
error = Marshal.GetLastWin32Error();
}
if ((result == IntPtr.Zero) && (error != 0))
{
throw new System.ComponentModel.Win32Exception(error);
}
return result;
}
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
private static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
private static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);
private static int IntPtrToInt32(IntPtr intPtr)
{
return unchecked((int)intPtr.ToInt64());
}
[DllImport("kernel32.dll", EntryPoint = "SetLastError")]
public static extern void SetLastError(int dwErrorCode);
#endregion
I've found a solution, but it's not pretty. So far this is the only thing I've tried that actually works:
Window w = new Window(); // Create helper window
w.Top = -100; // Location of new window is outside of visible part of screen
w.Left = -100;
w.Width = 1; // size of window is enough small to avoid its appearance at the beginning
w.Height = 1;
w.WindowStyle = WindowStyle.ToolWindow; // Set window style as ToolWindow to avoid its icon in AltTab
w.Show(); // We need to show window before set is as owner to our main window
this.Owner = w; // Okey, this will result to disappear icon for main window.
w.Hide(); // Hide helper window just in case
Found it here.
A more general, reusable solution would be nice. I suppose you could create a single window 'w' and reuse it for all windows in your app that need to be hidden from the Alt+↹Tab.
Update: Ok so what I did was move the above code, minus the this.Owner = w
bit (and moving w.Hide()
immediately after w.Show()
, which works fine) into my application's constructor, creating a public static Window
called OwnerWindow
. Whenever I want a window to exhibit this behavior, I simply set this.Owner = App.OwnerWindow
. Works great, and only involves creating one extra (and invisible) window. You can even set this.Owner = null
if you want the window to reappear in the Alt+↹Tab dialog.
Thanks to Ivan Onuchin over on MSDN forums for the solution.
Update 2: You should also set ShowInTaskBar=false
on w
to prevent it from flashing briefly in the taskbar when shown.
Here's what does the trick, regardless of the style of the window your are trying to hide from Alt+↹Tab.
Place the following into the constructor of your form:
// Keep this program out of the Alt-Tab menu
ShowInTaskbar = false;
Form form1 = new Form ( );
form1.FormBorderStyle = FormBorderStyle.FixedToolWindow;
form1.ShowInTaskbar = false;
Owner = form1;
Essentially, you make your form a child of an invisible window which has the correct style and ShowInTaskbar setting to keep out of the Alt-Tab list. You must also set your own form's ShowInTaskbar property to false. Best of all, it simply doesn't matter what style your main form has, and all tweaking to accomplish the hiding is just a few lines in the constructor code.
Inside your form class, add this:
protected override CreateParams CreateParams
{
get
{
var Params = base.CreateParams;
Params.ExStyle |= WS_EX_TOOLWINDOW;
return Params;
}
}
It's as easy as that; works a charm!