从Alt-Tab程序切换器隐藏窗口的最佳方法是什么?

时间:2008-12-10 18:31:40

标签: .net wpf winforms alt-tab

我已经成为.NET开发人员已有好几年了,这仍然是我不知道如何正确完成的事情之一。通过Windows窗体和WPF中的属性很容易从任务栏隐藏窗口,但据我所知,这并不能保证(或必然会影响)它从 Alt + ↹Tab对话框。我见过隐形窗口显示在 Alt + ↹Tab中,我只是想知道什么是保证窗口的最佳方法永远不会 Alt + ↹Tab对话框中出现(可见或不可见)。

更新:请参阅下面发布的解决方案。我不允许将自己的答案标记为解决方案,但到目前为止它是唯一有效的答案。

更新2: Franci Penov现在有一个看起来很不错的正确解决方案,但是我自己没试过。涉及一些Win32,但避免了屏幕外窗口的蹩脚创建。

13 个答案:

答案 0 :(得分:85)

<强>更新

根据@donovan,现代WPF通过设置原生支持 XAML中的ShowInTaskbar="False"Visibility="Hidden"。 (我还没有对此进行测试,但仍决定提高评论的可见性)

原始回答:

在Win32 API中有两种隐藏任务切换器窗口的方法:

  1. 添加WS_EX_TOOLWINDOW扩展窗口样式 - 这是正确的方法。
  2. 使其成为另一个窗口的子窗口。
  3. 不幸的是,WPF不支持像Win32一样灵活地控制窗口样式,因此WindowStyle=ToolWindow的窗口最终会使用默认的WS_CAPTIONWS_SYSMENU样式,这会导致它有一个标题和一个关闭按钮。另一方面,您可以通过设置WindowStyle=None来删除这两种样式,但不会设置WS_EX_TOOLWINDOW扩展样式,并且不会从任务切换器隐藏窗口。

    要使WindowStyle=None的WPF窗口也在任务切换器中隐藏,可以采用以下两种方式之一:

    • 使用上面的示例代码,使窗口成为一个小隐藏工具窗口的子窗口
    • 修改窗口样式以包含WS_EX_TOOLWINDOW扩展样式。

    我个人更喜欢第二种方法。然后,我做了一些高级的东西,比如在客户区扩展玻璃并在标题中启用WPF绘图,所以一点点互操作不是一个大问题。

    以下是Win32互操作解决方案方法的示例代码。首先,XAML部分:

    <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" >
    

    这里没什么特别的,我们只是声明一个WindowStyle=NoneShowInTaskbar=False的窗口。我们还为Loaded事件添加了一个处理程序,我们将修改扩展窗口样式。我们不能在构造函数中完成这项工作,因为那时还没有窗口句柄。事件处理程序本身非常简单:

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

    和Win32互操作声明。我已从枚举中删除了所有不必要的样式,只是为了使示例代码保持较小。此外,遗憾的是,在Windows XP上的user32.dll中找不到SetWindowLongPtr入口点,因此通过SetWindowLong路由呼叫的技巧。

        #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
    

答案 1 :(得分:37)

在表单类中,添加以下内容:

protected override CreateParams CreateParams
{
    get
    {
        var Params = base.CreateParams;
        Params.ExStyle |= 0x80;
        return Params;
    }
}

就这么简单;有魅力!

答案 2 :(得分:19)

我找到了一个解决方案,但它并不漂亮。到目前为止,这是我尝试过的唯一的实际上有用的东西:

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

找到它here

更通用,可重复使用的解决方案会很好。我想你可以创建一个单独的窗口'w',并将其重用于应用程序中需要隐藏 Alt + ↹Tab的所有窗口。

更新:好的,我所做的就是移动上面的代码,减去this.Owner = w位(并在w.Hide()之后立即移动w.Show(),这样可以正常工作)进入我的应用程序的构造函数,创建一个名为Window的公共静态OwnerWindow。每当我想要一个窗口展示这种行为时,我只需设置this.Owner = App.OwnerWindow。效果很好,只涉及创建一个额外(和不可见)窗口。如果您希望窗口重新出现在 Alt + ↹Tab对话框中,您甚至可以设置this.Owner = null

感谢Ivan Onuchin在MSDN论坛上的解决方案。

更新2:您还应在ShowInTaskBar=false上设置w,以防止它在显示时在任务栏中短暂闪烁。

答案 3 :(得分:10)

为什么这么复杂? 试试这个:

me.FormBorderStyle = FormBorderStyle.SizableToolWindow
me.ShowInTaskbar = false

从这里获取的想法:http://www.csharp411.com/hide-form-from-alttab/

答案 4 :(得分:8)

这就是诀窍,无论你试图隐藏的窗口风格如何 Alt + ↹Tab

将以下内容放入表单的构造函数中:

// Keep this program out of the Alt-Tab menu

ShowInTaskbar = false;

Form form1 = new Form ( );

form1.FormBorderStyle = FormBorderStyle.FixedToolWindow;
form1.ShowInTaskbar = false;

Owner = form1;

基本上,您使表单成为不可见窗口的子窗口,该窗口具有正确的样式和ShowInTaskbar设置,以防止超出Alt-Tab列表。您还必须将自己的表单的ShowInTaskbar属性设置为false。最重要的是,它与您的主要表单的样式无关,并且完成隐藏的所有调整只是构造函数代码中的几行。

答案 5 :(得分:3)

为什么要尝试这么多代码? 只需将FormBorderStyle属性设置为FixedToolWindow即可。 希望它有所帮助。

答案 6 :(得分:2)

看到它:(来自http://bytes.com/topic/c-sharp/answers/442047-hide-alt-tab-list#post1683880

[DllImport("user32.dll")]
public static extern int SetWindowLong( IntPtr window, int index, int
value);
[DllImport("user32.dll")]
public static extern int GetWindowLong( IntPtr window, int index);


const int GWL_EXSTYLE = -20;
const int WS_EX_TOOLWINDOW = 0x00000080;
const int WS_EX_APPWINDOW = 0x00040000;

private System.Windows.Forms.NotifyIcon notifyIcon1;


// I use two icons depending of the status of the app
normalIcon = new Icon(this.GetType(),"Normal.ico");
alertIcon = new Icon(this.GetType(),"Alert.ico");
notifyIcon1.Icon = normalIcon;

this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
this.Visible = false;
this.ShowInTaskbar = false;
iconTimer.Start();

//Make it gone frmo the ALT+TAB
int windowStyle = GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, windowStyle | WS_EX_TOOLWINDOW);

答案 7 :(得分:1)

在XAML中设置ShowInTaskbar =“False”:

<Window x:Class="WpfApplication5.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    ShowInTaskbar="False"    
    Title="Window1" Height="300" Width="300">
    <Grid>

    </Grid>
</Window>

编辑:我仍然在Alt + Tab中显示它,我想,不在任务栏中。

答案 8 :(得分:1)

我尝试将主窗体的可见性设置为false,只要它自动更改为true:

private void Form1_VisibleChanged(object sender, EventArgs e)
{
    if (this.Visible)
    {
        this.Visible = false;
    }
}

完美无缺:)

答案 9 :(得分:0)

Form1属性:
FormBorderStyle:相当大的 WindowState:最小化
ShowInTaskbar:False

private void Form1_Load(object sender, EventArgs e)
{
   // Making the window invisible forces it to not show up in the ALT+TAB
   this.Visible = false;
}>

答案 10 :(得分:0)

如果您希望表单无边框,则需要将以下语句添加到表单的构造函数中:

this.FormBorderStyle = FormBorderStyle.None;
this.ShowInTaskbar = false;

您必须将以下方法添加到派生的Form类中:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        // turn on WS_EX_TOOLWINDOW style bit
        cp.ExStyle |= 0x80;
        return cp;
    }
}

more details

答案 11 :(得分:0)

不要显示表格。使用隐身。

更多信息:http://code.msdn.microsoft.com/TheNotifyIconExample

答案 12 :(得分:-1)

就个人而言,据我所知,如果不以某种方式挂钩窗户是不可能的,我甚至不确定如何做到这一点或是否有可能。

根据您的需要,将应用程序上下文开发为NotifyIcon(系统托盘)应用程序将允许它在ALT + TAB中显示而不显示。但是,如果您打开表单,该表单仍将遵循标准功能。

如果你愿意,我可以在博客上挖掘一篇关于创建一个默认只有NotifyIcon的应用程序的文章。