Flash the title bar or taskbar button.

For applications that perform long operations or have some asynchronous operation such as a chat client or download manager, the standard way of alerting the user to the completion of a task, especially if the window is minimized, is to flash the taskbar button.

The .NET framework doesn't provide this facility in a managed class and so a bit of interop is needed to enable the use of the FlashWindowEx method.

FlashWindowEx is the Win32 API call that controls the flashing of the taskbar buttons or window title bar. This method takes a single parameter, the FLASHWINFO structure that contains all the options for the flash operation. In order to use the method you have to create a p/invoke method wrapper for the FlashWindowEx method and recreate the FLASHWINFO structure so that it's usable by managed code.

Recreating FLASHWINFO

The original Win32 FLASHWINFO structure is declared like so:

typedef struct {
  UINT cbSize;
  HWND hwnd;
  DWORD dwFlags;
  UINT uCount;
  DWORD dwTimeout;
} FLASHWINFO, 
*PFLASHWINFO;

Recreating this for managed code we need to change the types in the original structure for equivalent .NET types. This provides the following:

 

  [StructLayout(LayoutKind.Sequential)]

  public struct FLASHWINFO

  {

    public UInt32 cbSize; 

    public IntPtr hwnd; 

    public Int32 dwFlags; 

    public UInt32 uCount; 

    public Int32 dwTimeout;

  }

   <StructLayout(LayoutKind.Sequential)>  _

   Public Structure FLASHWINFO

      Public cbSize As UInt32

      Public hwnd As IntPtr

      Public dwFlags As Int32

      Public uCount As UInt32

      Public dwTimeout As Int32

   End Structure 'FLASHWINFO

The flags set in the FLASHWINFO structure dictate how the window is flashed and so the set of constant values from winuser.h, the old Win32 header file, can be brought into the .net world like so:

public enum FLASHWINFOFLAGS

  {

    FLASHW_STOP         =0,

    FLASHW_CAPTION      =0x00000001,

    FLASHW_TRAY         =0x00000002,

    FLASHW_ALL          =(FLASHW_CAPTION | FLASHW_TRAY),

    FLASHW_TIMER        =0x00000004,

    FLASHW_TIMERNOFG    =0x0000000C

  }

   Public Enum FLASHWINFOFLAGS

      FLASHW_STOP = 0

      FLASHW_CAPTION = &H1

      FLASHW_TRAY = &H2

      FLASHW_ALL = FLASHW_CAPTION Or FLASHW_TRAY

      FLASHW_TIMER = &H4

      FLASHW_TIMERNOFG = &HC

   End Enum 'FLASHWINFOFLAGS

To call the FlashWindowEx method the following code must be included in the form class. It creates a method wrapper for the Win32 API call.

    [DllImport("user32.dll")]

    public static extern int FlashWindowEx(ref FLASHWINFO pfwi);

    <DllImport("user32.dll")> _

    Public Shared Function FlashWindowEx(ByRef pfwi As FLASHWINFO) As Integer

    End Function

Finally, to demonstrate the use of this API the code shown in the following listing is an application that handles the OnSizeChanged event of the form. The form runs a timer which expires after five seconds. Normally, this timer is turned off but when the form is minimized it is set running so that five seconds after the minimize is detected, the taskbar button for the application will begin to flash to remind you that it's there.

using System;

using System.Drawing;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

using System.Runtime.InteropServices;

 

namespace FlashWindowDemo

{

  /// <summary>

  /// Summary description for Form1.

  /// </summary>

  public class Form1 : System.Windows.Forms.Form

  {

 

 

    [DllImport("user32.dll")]

    public static extern int FlashWindowEx(ref FLASHWINFO pfwi);

 

    private System.Windows.Forms.Timer timer1;

    private System.Windows.Forms.Label label1;

    private System.ComponentModel.IContainer components;

 

    public Form1()

    {

      //

      // Required for Windows Form Designer support

      //

      InitializeComponent();

 

      //

      // TODO: Add any constructor code after InitializeComponent call

      //

    }

 

 

    /// <summary>

    /// Clean up any resources being used.

    /// </summary>

    protected override void Dispose( bool disposing )

    {

      if( disposing )

      {

        if (components != null)

        {

          components.Dispose();

        }

      }

      base.Dispose( disposing );

    }

 

    #region Windows Form Designer generated code

    /// <summary>

    /// Required method for Designer support - do not modify

    /// the contents of this method with the code editor.

    /// </summary>

    private void InitializeComponent()

    {

      this.components = new System.ComponentModel.Container();

      this.timer1 = new System.Windows.Forms.Timer(this.components);

      this.label1 = new System.Windows.Forms.Label();

      this.SuspendLayout();

      //

      // timer1

      //

      this.timer1.Enabled = true;

      this.timer1.Interval = 5000;

      this.timer1.Tick += new System.EventHandler(this.timer1_Tick);

      //

      // label1

      //

      this.label1.Location = new System.Drawing.Point(32, 40);

      this.label1.Name = "label1";

      this.label1.Size = new System.Drawing.Size(232, 32);

      this.label1.TabIndex = 0;

      this.label1.Text = "Please minimise this window...";

      //

      // Form1

      //

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

      this.ClientSize = new System.Drawing.Size(292, 273);

      this.Controls.Add(this.label1);

      this.Name = "Form1";

      this.Text = "Form1";

      this.ResumeLayout(false);

 

    }

    #endregion

 

    /// <summary>

    /// The main entry point for the application.

    /// </summary>

    [STAThread]

    static void Main()

    {

      Application.Run(new Form1());

    }

 

    bool active;

 

    private void timer1_Tick(object sender, System.EventArgs e)

    {

      if(!active)

      {

        this.timer1.Enabled=false;

 

        FLASHWINFO fw=new FLASHWINFO();

        fw.cbSize=Convert.ToUInt32(Marshal.SizeOf(typeof(FLASHWINFO)));

        fw.hwnd=this.Handle;

        fw.dwFlags=(Int32)(FLASHWINFOFLAGS.FLASHW_ALL | FLASHWINFOFLAGS.FLASHW_TIMERNOFG);

        fw.dwTimeout=0;

 

        FlashWindowEx(ref fw);

      }

 

    }

 

    protected override void OnActivated(EventArgs e)

    {

      active=true;

      base.OnActivated (e);

    }

 

    protected override void OnDeactivate(EventArgs e)

    {

      this.timer1.Enabled=true;

      active=false;

      base.OnDeactivate (e);

    }

 

 

  }

 

  public enum FLASHWINFOFLAGS

  {

    FLASHW_STOP         =0,

    FLASHW_CAPTION      =0x00000001,

    FLASHW_TRAY         =0x00000002,

    FLASHW_ALL          =(FLASHW_CAPTION | FLASHW_TRAY),

    FLASHW_TIMER        =0x00000004,

    FLASHW_TIMERNOFG    =0x0000000C

  }

 

  [StructLayout(LayoutKind.Sequential)]

  public struct FLASHWINFO

  {

    public UInt32 cbSize; 

    public IntPtr hwnd; 

    public Int32 dwFlags; 

    public UInt32 uCount; 

    public Int32 dwTimeout;

  }

}

 

 

Imports System

Imports System.Drawing

Imports System.Collections

Imports System.ComponentModel

Imports System.Windows.Forms

Imports System.Data

Imports System.Runtime.InteropServices

 

 

Namespace FlashWindowDemo

   '/ <summary>

   '/ Summary description for Form1.

   '/ </summary>

  

   Public Class Form1

    Inherits System.Windows.Forms.Form

    

    

    

    <DllImport("user32.dll")> _

    Public Shared Function FlashWindowEx(ByRef pfwi As FLASHWINFO) As Integer

    End Function

 

    Private WithEvents timer1 As System.Windows.Forms.Timer

    Private label1 As System.Windows.Forms.Label

    Private components As System.ComponentModel.IContainer

 

 

    Public Sub New()

      '

      ' Required for Windows Form Designer support

      '

      InitializeComponent()

    End Sub 'New

 

    '

    ' TODO: Add any constructor code after InitializeComponent call

    '

 

 

    '/ <summary>

    '/ Clean up any resources being used.

    '/ </summary>

    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

      If disposing Then

        If Not (components Is Nothing) Then

          components.Dispose()

        End If

      End If

      MyBase.Dispose(disposing)

    End Sub 'Dispose

 

#Region "Windows Form Designer generated code"

 

    '/ <summary>

    '/ Required method for Designer support - do not modify

    '/ the contents of this method with the code editor.

    '/ </summary>

    Private Sub InitializeComponent()

      Me.components = New System.ComponentModel.Container

      Me.timer1 = New System.Windows.Forms.Timer

      Me.label1 = New System.Windows.Forms.Label

      Me.SuspendLayout()

      '

      ' timer1

      '

      Me.timer1.Enabled = True

      Me.timer1.Interval = 5000

      '

      ' label1

      '

      Me.label1.Location = New System.Drawing.Point(32, 40)

      Me.label1.Name = "label1"

      Me.label1.Size = New System.Drawing.Size(232, 32)

      Me.label1.TabIndex = 0

      Me.label1.Text = "Please minimise this window..."

      '

      ' Form1

      '

      Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

      Me.ClientSize = New System.Drawing.Size(292, 273)

      Me.Controls.Add(label1)

      Me.Name = "Form1"

      Me.Text = "Form1"

      Me.ResumeLayout(False)

    End Sub 'InitializeComponent

#End Region

 

 

    '/ <summary>

    '/ The main entry point for the application.

    '/ </summary>

    <STAThread()> _

    Shared Sub Main()

      Application.Run(New Form1)

    End Sub 'Main

 

    Private active As Boolean

 

 

    Private Sub timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles timer1.Tick

      If Not active Then

        Me.timer1.Enabled = False

 

        Dim fw As New FLASHWINFO

        fw.cbSize = Convert.ToUInt32(Marshal.SizeOf(GetType(FLASHWINFO)))

        fw.hwnd = Me.Handle

        fw.dwFlags = CType(FLASHWINFOFLAGS.FLASHW_ALL Or FLASHWINFOFLAGS.FLASHW_TIMERNOFG, Int32)

        fw.dwTimeout = 0

 

        FlashWindowEx(fw)

      End If

    End Sub 'timer1_Tick

 

 

    Protected Overrides Sub OnActivated(ByVal e As EventArgs)

      active = True

      MyBase.OnActivated(e)

    End Sub 'OnActivated

 

 

    Protected Overrides Sub OnDeactivate(ByVal e As EventArgs)

      Me.timer1.Enabled = True

      active = False

      MyBase.OnDeactivate(e)

    End Sub 'OnDeactivate

  End Class 'Form1

  

  

  

   Public Enum FLASHWINFOFLAGS

    FLASHW_STOP = 0

    FLASHW_CAPTION = &H1

    FLASHW_TRAY = &H2

    FLASHW_ALL = FLASHW_CAPTION Or FLASHW_TRAY

    FLASHW_TIMER = &H4

    FLASHW_TIMERNOFG = &HC

   End Enum 'FLASHWINFOFLAGS

  

   <StructLayout(LayoutKind.Sequential)>  _

   Public Structure FLASHWINFO

    Public cbSize As UInt32

    Public hwnd As IntPtr

    Public dwFlags As Int32

    Public uCount As UInt32

    Public dwTimeout As Int32

   End Structure 'FLASHWINFO

End Namespace 'FlashWindowDemo

For full details on the operation of FlashWindowEx and the FLASHWINFO structure and it's related flags check out the MSDN page that describes the method.

Return to Bob Powell's Windows Forms Tips and Tricks