63

What is the proper way to minimize a WinForms app to the system tray?

Note: minimize to system tray; on the right side of the taskbar by the clock. I'm not asking about minimizing to taskbar, which is what happens when you hit the "minus" button on the window.

I've seen hackish solutions like, "minimize, set ShowInTaskbar = false, then show your NotifyIcon."

Solutions like that are hackish because the app doesn't appear to minimize to the tray like other apps, the code has to detect when to set ShowInTaskbar = true, among other issues.

What's the proper way to do this?

Judah Gabriel Himango
  • 58,906
  • 38
  • 158
  • 212
  • 1
    Could you provide the name of a sample application that does what you are looking for. I really don't think there is a "proper" way to do this since your really shouldn't be minimizing applications to the notification area anyway. – Navaar Jun 13 '09 at 19:52
  • 1
    Perhaps you and I are using different terminology. I'm speaking of the area in the system tray where apps like WinRar, Vuze, Witty, Folding @ Home, etc. minimize and/or show information. – Judah Gabriel Himango Jun 14 '09 at 04:09
  • Hi Judah! I was just looking at the animation in my Windows 7 Ent (x64) system when I minimized a few apps to the tray. It seems that all of the ones I checked use the close animation. If you look carefully they just shrink down to nothing. I checked Skype and MS Office Communicator. They both use the close animation. I also checked Miranda Portable, but it does not animate at all. Can you point me to an app that actually animates to the tray? Anyway, based on what I saw it looks like you should just close your form and keep the thread alive, which is described in FlySwat's post. – i did not pay the royalties Feb 02 '11 at 08:20
  • @JudahHimango: According to Microsoft employee Ivan Brugiolo "When desktop composition is enabled, DrawAnumatedRects is a nop in Vista." So what do you use in Vista and Windows 7? – Giorgi Nov 12 '12 at 19:00
  • @Giorgi, I'm not using this code anymore, not writing desktop apps anymore. Sorry! – Judah Gabriel Himango Nov 12 '12 at 19:08

9 Answers9

20

There is actually no managed way to do that form of animation to the tray in native winforms, however you can P/Invoke shell32.dll to do it:

Some good info here (In the comments not the post):

http://blogs.msdn.com/jfoscoding/archive/2005/10/20/483300.aspx

And here it is in C++:

http://www.codeproject.com/KB/shell/minimizetotray.aspx

You can use that to figure out what stuff to Pinvoke for your C# version.

RCIX
  • 38,647
  • 50
  • 150
  • 207
FlySwat
  • 172,459
  • 74
  • 246
  • 311
  • P/Invoke isn't "hackish?" It's more hackish than using some built-in way to to do this. :) – bugfixr Sep 06 '11 at 12:04
  • 6
    Calling the correct Win32 API isn't hackish. It just means the .NET framework base class library designers missed this area, didn't deem it important enough to wrap it themselves. – Judah Gabriel Himango Dec 12 '11 at 19:21
7
this.WindowState = FormWindowState.Minimized  

That is the built in way to do it and it looks fine to me most of the time. The only time is has some weirdness to it is if you call it on startup which has some weirdness sometimes which is why most people will also set the ShowInTaskbar = false and hide the form too.

http://msdn.microsoft.com/en-us/library/system.windows.forms.form.windowstate.aspx

Ryan Farley
  • 11,315
  • 4
  • 46
  • 43
  • 2
    Ryan, that minimizes it to the taskbar, not the system tray. If you additionally call ShowInTaskbar = false, you get into the hackish scenario described in the post. I want it to actually minimize to the system tray. E.g. if I minimize, I want Windows to show that it is minimizing to the system tray, not to the task bar or start menu. Most apps that minimize to the tray do this just fine; Windows draws the window minimizing to the system tray. How are they doing it? – Judah Gabriel Himango Jun 05 '09 at 15:03
5

It will helps:

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;
        }
    }
}
Jimi
  • 29,621
  • 8
  • 43
  • 61
Jeyavel
  • 2,974
  • 10
  • 38
  • 48
4

This code is tested and supports many options.

More here: http://code.msdn.microsoft.com/TheNotifyIconExample

3

Update: Looks like I posted too soon. I was also using the below hack for a tool of mine. Waiting for the right solution for this..........

You can use Windows.Forms.NotifyIcon for this. http://msdn.microsoft.com/en-us/library/system.windows.forms.notifyicon.aspx

Add NotifyIcon to the form, set some properties and you are done.

        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();
    }
Gulzar Nazim
  • 51,744
  • 26
  • 128
  • 170
  • 1
    Gulzar, your code is exactly the hackish scenario I described in the post. Is there a better way? I'm looking for a *real* minimize to tray, not this "show notify icon & don't ShowInTaskbar" hack. – Judah Gabriel Himango Jun 05 '09 at 15:03
1

Similar as above...

I have a resize event handler that is triggered whenever the window gets resized (Maximized, minimized, etc.)...

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

This allows the user to select whether or not they want to minimize to tray via a menubar. They can go to Windows -> and click on Minimize to Tray. If this is checked, and the window is being resized to Minimized, then it will minimize to the tray. Works flawlessly for me.

  • Kevin, I think you misunderstood my question. I am asking for the proper way to minimize. By "proper", I mean having Windows play the minimize to tray animation for your window as it's minimized. Your solution plays the standard "minimize to task bar", and then the taskbar tile disappears. – Judah Gabriel Himango Feb 11 '09 at 15:24
0

I have programmed a quick solution for you guys, this might not be the most efficient way (I'm not sure), but it definitely works !

The below solution successfully minimizes your Winforms application to the system Tray, and by using the notify Icon you create a small icon in the system tray with the aforementioned menu items. than we use the in-built libraries to fetch the Window (GUI in this case), and using the appropriate methods with Booleans we then minimize the window to the tray.

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();
        }
KingOverTheHill
  • 11
  • 1
  • 10
  • Welcome to StackOverflow. Please provide some explanation how does your solution might solve the OP's problem. – Peter Csala Jul 22 '20 at 17:18
  • 1
    :) Thanks. the above solution successfully minimizes your Winforms application to the system Tray, and by using the notify Icon you create a small icon in the system tray with the aforementioned menu items. than we use the in-built libraries to fetch the Window (GUI in this case), and using the appropriate methods with Booleans we then minimize the window to the tray. – Brian_The_Programmer Jul 23 '20 at 19:00
  • Please add this description to the answer. – Peter Csala Jul 23 '20 at 20:18
-1

If you are having problems getting this to work, check that you have

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

in the fom1.designer.cs

robert
  • 33,242
  • 8
  • 53
  • 74
James
  • 1
-1

In the constructor of the Form:

this.Resize += new EventHandler(MainForm_Minimize);

Then use this Event Handler method:

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