Share via


Notify applications where the form disappears from the taskbar on minimize

Jim asks:

"I've been following your Windows Forms articles, and was hoping that you could post about the correct way to minimize a Windows Form to the tray. The ShowInTask property of the Form is mentioned in newsgroups, but the Form still shows in the Alt+Tab list."

The best way to handle this situation is to actually close the form. Then the form cannot possibly be in the ALT+Tab list. This article discusses how to build notify applications which close the form on minimize

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

Note that this would usually end your application - you'll either need to use Application.Run() instead or a custom ApplicationContext. This is discussed in the above article.

"Also, I've noticed that applications such as Messenger animate the window correctly towards the tray, which is different from the WindowState/ShowInTaskBar behavior."

Here my recommendation would be to cancel the closing event on the form and use a System.Windows.Forms.Timer to shrink the form to the bottom right of the working area. The rough idea would be

     System.Windows.Forms.Timer timer = new Timer(components);

   private bool animationComplete = false;

   private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// in 2.0 could also check if e.CloseReason == CloseReason.UserClosing
// in 1.1 can use the Form.Closing event instead.
            if (!animationComplete) {
e.Cancel = true;
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 10;
timer.Enabled = true;
}
}
           

   // you may have to play with this code to get the desired effect you want
     void timer_Tick(object sender, EventArgs e) {
Rectangle workingArea = Screen.GetWorkingArea(this.Bounds);

            this.Bounds = new Rectangle(
Math.Max(workingArea.Width - this.Width, this.Left + 10),
Math.Max(workingArea.Height - this.Height, this.Top + 10),
Math.Max(0, this.Width - 10),
Math.Max(0, this.Height-10));

            if (this.Bounds.Bottom >= workingArea.Bottom) {
timer.Tick -= new EventHandler(timer_Tick);
timer.Enabled = false;
                animationComplete = true;
this.Close();
}

}
}

Hope this helps! 

Related Article: https://www.windowsforms.net/articles/notifyiconapplications.aspx

Comments

  • Anonymous
    October 20, 2005
    Of course, this animation only works if your taskbar is on the bottom of the screen.
  • Anonymous
    October 21, 2005
    The comment has been removed
  • Anonymous
    October 21, 2005
    Ooooo Ben you caught me putting together a 5 minute quick sample. Good catch
  • Anonymous
    October 21, 2005
    The comment has been removed
  • Anonymous
    October 21, 2005
    Basically, what I'm trying to do is to have my application behave the same way that Messenger does (startup in tray, mimimize to tray). From the looks of it, I would say that Messenger uses the Shell to animate the mimimize to tray action, and not a custom animation of the window bounds.

    As a side note, I can't close the form since I need a message queue to listen for DDE messages from an external application. Also, I found that I need to use Form.Show() in order to get notified in the OnClose event when the user is logging out, or windows is shutting down. So when my app starts minimized to tray, I show it offscreen before using ShowInTaskBar=false and WindowState=Minimized to place it in the tray.
  • Anonymous
    October 21, 2005
    Regarding the message window, you can have a hidden ContainerControl with SetTopLevel(true), and Visible = false that is tied to the lifetime of your application context. It does not have to be your form.

    Regarding a more automagic API, I'm not aware of one that is built into the Win32 API, perhaps someone else is...

    The AnimateWindow API may get you pretty close - I'm not sure if this gives you 100% of the automaticness you want.

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/animatewindow.asp
  • Anonymous
    October 21, 2005
    sigh No need to get snippy. What I perhaps should have said was: "How do I find out the location of the taskbar/status tray so I can corrently animate towards it wherever it may be?" A tangent, I know, but as one of the few who run their taskbars docked to different edges of the screen, it has become apparent that very many developers hardcode things to assume the taskbar is at the bottom.
  • Anonymous
    October 21, 2005
    Thanks for the articles, very useful. I have created a custom context for my application and every thing is working well, one small side effect I noticed is when using tooltips on the notify icons context menu, the tooltips are displayed behind the menu, incorrect z-order. I don't think tooltips are necessary as the menu items are self explanatory, have you see this behaviour?
  • Anonymous
    October 22, 2005
    Ah Ben! Sorry about that, I didnt mean to come off as "snippy". You totally called me out for a bad sample and I encourage everyone to do that when I mess up.

    You'll have to dig into the shell APIs to figure out the exact location of the start bar.

    The ABM_GETTASKBARPOS gets you pretty close

    NativeMethods.APPBARDATA appBarData = NativeMethods.APPBARDATA.Create();
    NativeMethods.SHAppBarMessage(NativeMethods.ABM_GETTASKBARPOS, ref appBarData);
    NativeMethods.RECT taskBarLocation = appBarData.rc;



    internal class NativeMethods {
    // All definitions taken from http://pinvoke.net

    [DllImport("shell32.dll")]
    public static extern IntPtr SHAppBarMessage(uint dwMessage, ref APPBARDATA pData);


    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(
    string lpClassName,
    string lpWindowName
    );
    public const string TaskbarClass = "Shell_TrayWnd";

    [StructLayout(LayoutKind.Sequential)]
    public struct APPBARDATA {
    public static APPBARDATA Create() {
    APPBARDATA appBarData = new APPBARDATA();
    appBarData.cbSize =Marshal.SizeOf(typeof(NativeMethods.APPBARDATA));
    return appBarData;
    }
    public int cbSize;
    public IntPtr hWnd;
    public uint uCallbackMessage;
    public uint uEdge;
    public RECT rc;
    public int lParam;
    }
    public const int ABM_QUERYPOS = 0x00000002,
    ABM_GETTASKBARPOS=5;
    public const int ABE_LEFT = 0;
    public const int ABE_TOP = 1;
    public const int ABE_RIGHT = 2;
    public const int ABE_BOTTOM = 3;


    [Serializable, StructLayout(LayoutKind.Sequential)]
    public struct RECT {
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;

    public RECT(int left_, int top_, int right_, int bottom_) {
    Left = left_;
    Top = top_;
    Right = right_;
    Bottom = bottom_;
    }

    public int Height { get { return Bottom - Top + 1; } }
    public int Width { get { return Right - Left + 1; } }
    public Size Size { get { return new Size(Width, Height); } }

    public Point Location { get { return new Point(Left, Top); } }

    // Handy method for converting to a System.Drawing.Rectangle
    public Rectangle ToRectangle() {
    return Rectangle.FromLTRB(Left, Top, Right, Bottom); }

    public static RECT FromRectangle(Rectangle rectangle) {
    return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
    }

    public override int GetHashCode() {
    return Left ^ ((Top << 13) | (Top >> 0x13))
    ^ ((Width << 0x1a) | (Width >> 6))
    ^ ((Height << 7) | (Height >> 0x19));
    }

    #region Operator overloads

    public static implicit operator Rectangle( RECT rect ) {
    return Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);
    }

    public static implicit operator RECT( Rectangle rect ) {
    return new RECT(rect.Left, rect.Top, rect.Right, rect.Bottom);
    }

    #endregion
    }
    }

    There are a couple of good articles on code project which talk about how to do this in unmanaged code.

    http://www.codeproject.com/shell/trayposition.asp
    http://www.codeproject.com/shell/minimizetotray.asp

    Hope this helps!

  • Anonymous
    October 22, 2005
    Thanks for the tip. I'm currently working on one of those msn-messenger-style popup notifications, and none of the ones I've found supported different taskbar locations - this will be very helpful!
  • Anonymous
    October 22, 2005
    Here's an interesting take on the Alt+Tab issue (I'm not sure how useful it is)

    From http://www.syncfusion.com/FAQ/WindowsForms/FAQ_c84c.aspx

    18.34 I add my application to the Window's Tray, set ShowInTaskBar to false, but the program still appears in the Alt+Tab list.


    An easy way to keep the program from appearing in the Alt+Tab list is to set the Form's FormBorderStyle property to be a ToolWindow (fixed or sizable).

    One caveat to this is that if you display another Form (that is not a ToolWindow) through a call in your application, the main Form will again appear in the Alt+Tab listing. To get around this, you can make all of the Forms in your Tray application have a FormBorderStyle of ToolWindow.
  • Anonymous
    October 23, 2005
    Mark -

    If you're using tooltips on the MenuItems I'm assuming you're using ContextMenuStrip? There was a problem with Beta2 Windows Forms 2.0 where the ToolTips were not set to HWND_TOPMOST, therefore would fall behind the ContextMenuStrip.

    If you're using something else or a later version, please let me know.
  • Anonymous
    October 23, 2005
    Ben -

    Thanks for bringing this up. If you're building a "toaster window", this is probably the right thing to do.

    The official support article for this is:
    http://support.microsoft.com/default.aspx?scid=kb;en-us;205158

    Note if you're using Windows Forms 2.0, just use a ToolStripDropDown instead. Like Form, the Close reason can be inspected/cancelled so you can change it from having ContextMenu like closing behavior.

    It will save you quite a bit of headache. If you want TableLayout - here's a sample of how to use it in the designer with the same properties as TableLayoutPanel.

    http://blogs.msdn.com/jfoscoding/articles/481306.aspx



  • Anonymous
    April 07, 2008
    http://blogs.msdn.com/jfoscoding/archive/2005/10/20/483300.aspx