Flashing Dialogboxes

This example demonstrates how to flash a dialog in the taskbar after performing a long running operation from the command line.  A good example of how this is useful is in the VS Team Foundation version control toolset.  Naturally, it includes a command line utility (TF.EXE) to perform source control operations.  Sometimes these operations can be very lengthy and in the end result in a dialog box popping up - performing a lengthy GET operation that results in conflicts to resolve is a good example of when this would happen.

Our own UI framework does quite a bit more than this - this is just a scaled down example of what happens when a dialog is invoked from the command line.  Many of our dialogs end up getting displayed in Visual Studio, standalone Windows apps, Excel, the command line, etc.  So in our framework the method to show a modal dialog is actually virtual so the appropriate thing happens in each environment.

  using System;
 using System.Diagnostics;
 using System.Text;
 using System.Windows.Forms;
 using System.Runtime.InteropServices;
 using System.Threading;
  
 namespace ConsoleFlasher
 {
     class Program
     {
         static void Main(string[] args)
         {
             Thread.Sleep(5000); //  Lengthy operation... 
             using (MyForm form = new MyForm())
             {
                 UIHost.ShowModalDialog(form);
             }
         }
     }
  
     static class UIHost
     {
         static public DialogResult ShowModalDialog(Form form)
         {
             return ShowModalDialog(form, null);
         }
  
         //********************************************************************************************
         /// <summary>
         /// Display a modal dialog a windows application.
         /// </summary>
         /// <param name="form">The form to display.</param>
         /// <param name="parent">The parent window for window stack ordering.</param>
         //********************************************************************************************
         static public DialogResult ShowModalDialog(Form form, IWin32Window parent)
         {
             bool isTopLevel = parent == null;
  
             form.ShowInTaskbar = isTopLevel;
             form.MinimizeBox = isTopLevel;
             form.ShowIcon = isTopLevel;
  
             if (isTopLevel)
             {
                 //  Attach an event handler so maybe we'll flash the title in the taskbar
                 form.Activated += new EventHandler(OnModalDialogFormActivate);
  
                 //  This is where you would also set your app icon on the dialog as well.
             }
         
             DialogResult result = form.ShowDialog(parent);
         
             if (isTopLevel)
             {
                 form.Activated -= new EventHandler(OnModalDialogFormActivate);
             }
         
             return result;
         }
  
         //********************************************************************************************
         /// <summary>
         /// On form activate for a modal dialog for the console.  We check to see if we are the 
         /// foreground window and flash the taskbar if we aren't.
         /// </summary>
         /// <param name="sender">Form to activate</param>
         /// <param name="e">Standard event args</param>
         //********************************************************************************************
         static private void OnModalDialogFormActivate(object sender, EventArgs e)
         {
             Form form = sender as Form;
  
             if (form != null)
             {
                 Debug.Assert(form.Visible, "Form should already be visible for FormActivate");
  
                 IntPtr foregroundHwnd = NativeMethods.GetForegroundWindow();
  
                 //  First see if we're in the foreground
  
                 if (foregroundHwnd != form.Handle)
                 {
                     IntPtr consoleHwnd = NativeMethods.GetConsoleWindow();
  
                     //  We're not in the foreground so let's see if our console is.
  
                     if (foregroundHwnd != consoleHwnd)
                     {
                         //  We're not in the foreground and neither is our console window so let's
                         //  flash the user.
                         int flashCount = 1;
  
                         //  make sure we read the count from the system and respect this for accessibility
                         NativeMethods.SystemParametersInfo(NativeMethods.SPI_GETFOREGROUNDFLASHCOUNT, 0, ref flashCount, 0);
  
                         NativeMethods.FLASHWINFO fwi = new NativeMethods.FLASHWINFO();
  
                         fwi.cbSize = (UInt32)Marshal.SizeOf(typeof(NativeMethods.FLASHWINFO));
                         fwi.dwFlags = NativeMethods.FLASHW_ALL;
                         fwi.dwTimeout = 0;      //  This will get the system default which is the caret blink rate
                         fwi.uCount = (UInt32)flashCount;
                         fwi.hwnd = form.Handle;
  
                         //  blinky, blinky, blinky
                         NativeMethods.FlashWindowEx(ref fwi);
                     }
                     else
                     {
                         //  Our console has the input focus so let's be a bit more agressive...
                         NativeMethods.SetForegroundWindow(new HandleRef(form, form.Handle));
                     }
                 }
  
                 //  Don't call us again...
                 form.Activated -= new EventHandler(OnModalDialogFormActivate);
             }
             else
             {
                 Debug.Fail("Invalid sender in OnModalDialogFormLoad");
             }
         }
  
     }
  
     static class NativeMethods
     {
         public const int SPI_GETFOREGROUNDFLASHCOUNT = 0x2004;
  
         public const int FLASHW_STOP = 0;
         public const int FLASHW_CAPTION = 0x00000001;
         public const int FLASHW_TRAY = 0x00000002;
         public const int FLASHW_ALL = (FLASHW_CAPTION | FLASHW_TRAY);
         public const int FLASHW_TIMER = 0x00000004;
         public const int FLASHW_TIMERNOFG = 0x0000000C;
  
         [StructLayout(LayoutKind.Sequential)]
         internal struct FLASHWINFO
         {
             public UInt32 cbSize;
             public IntPtr hwnd;
             public UInt32 dwFlags;
             public UInt32 uCount;
             public UInt32 dwTimeout;
         }
  
         [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
         public extern static int FlashWindowEx(ref FLASHWINFO fwi);
  
         [DllImport("user32", CharSet = CharSet.Auto)]
         public static extern bool SystemParametersInfo(int nAction, int nParam, ref int value, int ignore);
  
         [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
         internal static extern IntPtr SetForegroundWindow(HandleRef hwnd);
  
         [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
         internal static extern IntPtr GetForegroundWindow();
  
         [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
         internal static extern IntPtr GetConsoleWindow(); 
     }
 }