如何将C# WinForms应用程序最小化到托盘?

63

如何将WinForms应用程序最小化到系统托盘?

注意:最小化到系统托盘,即在任务栏右侧靠近时钟的位置。我不是在问如何最小化到任务栏,这是当你点击窗口上的“减号”按钮时发生的事情。

我见过像“最小化,设置ShowInTaskbar = false,然后显示你的NotifyIcon”这样的hackish解决方案。

这样的解决方案是hackish的,因为应用程序不会像其他应用程序一样最小化到托盘,代码必须检测何时设置ShowInTaskbar = true,还有其他问题。

什么是正确的方法呢?


1
你能提供一个样例应用程序的名称,它可以做到你所寻找的功能吗?我真的不认为有一种“正确”的方法来做这件事,因为你本来就不应该将应用程序最小化到通知区域。 - Navaar
1
也许你和我使用的术语不同。我说的是系统托盘中的区域,例如WinRar、Vuze、Witty、Folding@Home等应用程序最小化和/或显示信息的区域。 - Judah Gabriel Himango
嗨,Judah!我刚刚在我的Windows 7 Ent(x64)系统中观看动画时,将一些应用程序最小化到托盘中。似乎我检查的所有应用程序都使用关闭动画。如果你仔细看,它们只是缩小到无穷小。我检查了Skype和MS Office Communicator。它们都使用关闭动画。我还检查了Miranda Portable,但它根本没有动画。你能给我指出一个实际上可以动画到托盘的应用程序吗?无论如何,根据我所看到的,看起来你应该关闭你的表单并保持线程活动,这在FlySwat的帖子中有描述。 - i did not pay the royalties
根据微软员工伊万·布鲁吉奥洛的说法,“启用桌面组合时,在Vista中DrawAnimatedRects是nop”。那么在Vista和Windows 7中你使用什么? - Giorgi
@Giorgi,我不再使用这段代码了,也不再编写桌面应用程序了。抱歉! - Judah Gabriel Himango
9个回答

20

P/Invoke不是“hackish”吗?它比使用一些内置的方法更加“hackish”。 :) - bugfixr
6
调用正确的Win32 API不是hack。它只意味着.NET Framework基类库的设计人员忽略了这个领域,认为这个领域不足以自己来封装它。 - Judah Gabriel Himango

7

2
Ryan,这将其最小化到任务栏,而不是系统托盘。如果你额外调用ShowInTaskbar = false,你会陷入文章中描述的hackish情景。我希望它真的最小化到系统托盘。比如,如果我最小化了,我希望Windows显示它正在最小化到系统托盘,而不是任务栏或开始菜单。大多数最小化到托盘的应用程序都做得很好;Windows会绘制窗口最小化到系统托盘。他们是怎么做到的呢? - Judah Gabriel Himango

5

它将有助于:

public partial class Form1 : Form
{
    public static bool Close = false;
    Icon[] images;
    int offset = 0;

    public Form1()
    {
        InitializeComponent();

        notifyIcon1.BalloonTipText = "My application still working...";
        notifyIcon1.BalloonTipTitle = "My Sample Application";
        notifyIcon1.BalloonTipIcon = ToolTipIcon.Info; 
    }

    private void Form1_Resize(object sender, EventArgs e)
    {
        if (FormWindowState.Minimized == WindowState)
        {
            this.Hide();
            notifyIcon1.ShowBalloonTip(500);
            //WindowState = FormWindowState.Minimized;
        }
    }

    private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
    {
        this.Show();
        notifyIcon1.ShowBalloonTip(1000);
        WindowState = FormWindowState.Normal;
    }

    private void maximizeToolStripMenuItem_Click(object sender, EventArgs e)
    {
        this.Show();
        WindowState = FormWindowState.Normal;
    }

    private void closeToolStripMenuItem_Click(object sender, EventArgs e)
    {
        Close = true;
        this.Close();  
    }

    // Handle Closing of the Form.
    protected override void OnClosing(CancelEventArgs e)
    {
        if (Close)
        {
            e.Cancel = false;
        }
        else
        {
            WindowState = FormWindowState.Minimized;
            e.Cancel = true;
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        // Load the basic set of eight icons.
        images = new Icon[5];
        images[0] = new Icon("C:\\icon1.ico");
        images[1] = new Icon("C:\\icon2.ico");
        images[2] = new Icon("C:\\icon3.ico");
        images[3] = new Icon("C:\\icon4.ico");
        images[4] = new Icon("C:\\icon5.ico");
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        // Change the icon.
        // This event handler fires once every second (1000 ms).
        if (offset < 5)
        {
            notifyIcon1.Icon = images[offset];
            offset++;
        }
        else
        {
            offset = 0;
        }
    }
}

这就是我所说的hack。它并不是真正的最小化到托盘(而是最小化到任务栏),需要使用hackish代码来显示/隐藏任务栏图标。 - Judah Gabriel Himango

4

NotifyIconExample在2011年更新了吗?是否能够在VS 2010、XP和Win7上正常工作呢? - Kiquenet

3
更新:看起来我发帖太早了。我也在使用下面的方法来制作我的工具。等待这个问题的正确解决方案..........
您可以使用Windows.Forms.NotifyIcon来实现此功能。 http://msdn.microsoft.com/en-us/library/system.windows.forms.notifyicon.aspx
将NotifyIcon添加到窗体中,设置一些属性即可完成。
        this.ShowIcon = false;//for the main form
        this.ShowInTaskbar = false;//for the main form
        this.notifyIcon1.Visible = true;//for notify icon
        this.notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon")));//set an icon for notifyicon

    private void Form1_Shown(object sender, EventArgs e)
    {
        this.Hide();
    }

1
Gulzar,你的代码正是我在帖子中描述的hackish场景。有没有更好的方法?我正在寻找一个真正的最小化到托盘,而不是这个“显示通知图标并不在任务栏中显示”的hack。 - Judah Gabriel Himango

1

与上述类似...

我有一个调整大小事件处理程序,每当窗口调整大小(最大化、最小化等)时触发...

    public form1()
    {
       Initialize Component();

       this.Resize += new EventHanlder(form1_Resize);
    } 


    private void form1_Resize(object sender, EventArgs e)
    {
       if (this.WindowState == FormWindowState.Minimized && minimizeToTrayToolStripMenuItem.Checked == true)
       {
             NotificationIcon1.Visible = true;
             NotificationIcon1.BalloonTipText = "Tool Tip Text"
             NotificationIcon1.ShowBalloonTip(2);  //show balloon tip for 2 seconds
             NotificationIcon1.Text = "Balloon Text that shows when minimized to tray for 2 seconds";
             this.WindowState = FormWindowState.Minimized;
             this.ShowInTaskbar = false;
       }
    }

这允许用户通过菜单栏选择是否要最小化到托盘。他们可以转到Windows->并单击最小化到托盘。如果选中此选项,并且窗口正在调整大小以最小化,则它将最小化到托盘。对我来说运行得非常完美。


Kevin,我觉得你误解了我的问题。我是在问最小化的正确方式。所谓“正确”,是指当窗口最小化时,让Windows播放最小化到托盘的动画。你提供的解决方案只是播放标准的“最小化到任务栏”,然后任务栏图标消失了。 - Judah Gabriel Himango

0
我已经为你们编写了一个快速解决方案,这可能不是最有效的方法(我不确定),但它绝对有效!
以下解决方案成功地将您的Winforms应用程序最小化到系统托盘中,并使用通知图标在系统托盘中创建一个小图标,其中包含上述菜单项。然后我们使用内置库来获取窗口(在本例中为GUI),并使用适当的布尔方法将窗口最小化到托盘中。
private void TransformWindow()
        {
            ContextMenu Menu = new ContextMenu();

            Menu.MenuItems.Add(RestoreMenu);
            Menu.MenuItems.Add(HideMenu);
            Menu.MenuItems.Add(new MenuItem("Exit", new EventHandler(CleanExit)));

            notifyIcon = new NotifyIcon()
            {
                Icon = Properties.Resources.Microsft_Icon,
                Text = "Folder Watcher",
                Visible = true,
                ContextMenu = Menu
            };
            processHandle = Process.GetCurrentProcess().MainWindowHandle;
            ResizeWindow(false);

        }
        #region Getting Libraries
        [DllImport("user32.dll")]
        public static extern IntPtr GetShellWindow();
        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();
        public const Int32 SwMINIMIZE = 0;
        public const Int32 SwMaximize = 9;

        [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern IntPtr GetConsoleWindow();

        [DllImport("User32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ShowWindow([In] IntPtr hWnd, [In] Int32 nCmdShow);
        public static void MinimizeConsoleWindow()
        {
            IntPtr hWndConsole = processHandle;
            ShowWindow(hWndConsole, SwMINIMIZE);

        }
        public static void MaximizeConsoleWindow()
        {
            IntPtr hWndConsole = processHandle;
            ShowWindow(hWndConsole, SwMaximize);

        }
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
        public static void ResizeWindow(bool show)
        {
            RestoreMenu.Enabled = !show;
            HideMenu.Enabled = show;
            SetParent(processHandle, WinDesktop);

            if (show)
                MaximizeConsoleWindow();
            else
                MinimizeConsoleWindow();

        }
        public static void MinimizeClick(object sender, EventArgs e) => ResizeWindow(false);
        public static void MaximizeClick(object sender, EventArgs e) => ResizeWindow(true);
        public static IntPtr processHandle;
        public static IntPtr WinShell;
        public static IntPtr WinDesktop;
        public static NotifyIcon notifyIcon;
        public static MenuItem HideMenu = new MenuItem("Hide", new EventHandler(MinimizeClick));
        public static MenuItem RestoreMenu = new MenuItem("Restore", new EventHandler(MaximizeClick));
        public void CleanExit(object sender, EventArgs e)
        {
            notifyIcon.Visible = false;
            this.Close();
        }

欢迎来到StackOverflow。请提供一些解释,说明您的解决方案如何解决OP的问题。 - Peter Csala
1
以上解决方案成功地将您的Winforms应用程序最小化到系统托盘中,并通过使用通知图标在系统托盘中创建一个带有上述菜单项的小图标。然后,我们使用内置库来获取窗口(在本例中为GUI),并使用适当的布尔方法将窗口最小化到托盘中。 - Brian_The_Programmer
请将此描述添加到答案中。 - Peter Csala

-1
在表单的构造函数中:
this.Resize += new EventHandler(MainForm_Minimize);

然后使用这个事件处理方法:

    private void MainForm_Minimize(object sender, EventArgs e)
    {
        if(this.WindowState == FormWindowState.Minimized)
            Hide();
    }

结构化的,那不是我要找的。我正在尝试让应用程序最小化到托盘,使用 XP/Vista 动画来显示它进入托盘。 - Judah Gabriel Himango

-1

如果您在使用过程中遇到问题,请检查您是否已经

this.Resize += new System.EventHandler(this.Form1_Resize);

在fom1.designer.cs文件中


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接