Notifying the user that attention to your program is needed by
flashing the taskbar button, the window, or both.
If the application you are building is targeted at Windows 98 or above, you can use the
API call FlashWindowEx and pass it a FlashWInfo structure. If you are targeting
Windows 95 your options are more limited, and you have to use a timer to achieve the
desired result.
FlashWindowEx
A TFlashWInfo structure is needed to hold the information Windows needs to do its job
when you make the API call. Luckily all the work has been done for you, and Delphi
already knows about the structure. It is declared in Delphi like this:
type
TFlashWInfo = record
cbSize : LongInt;
hWnd : LongInt;
dwFlags : LongInt;
uCount : LongInt;
dwTimeout : LongInt;
end;
All you need to do is to populate the record and call the API function. For this
example I have placed both in the OnClick event of a button, but you can of course locate
them wherever suits you:
procedure TForm1.Button1Click(Sender: TObject);
var
FWinfo: TFlashWInfo;
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle; // Handle of Window to flash
FWinfo.dwflags := FLASHW_ALL;
FWinfo.ucount := 10; // number of times to flash
FWinfo.dwtimeout := 0; // speed in ms, 0 default blink cursor rate
FlashWindowEx(FWinfo); // make it flash!
end;
Note that the flag shown against the dwflags property determines what the call does
with the flashing, and the following constants are defined:
FLASHW_STOP = 0 // Stop flashing
FLASHW_CAPTION = 1 // Flash the window caption
FLASHW_TRAY = 2 // Flash the taskbar button
FLASHW_ALL = 3 // Flash both the window caption and taskbar button
FLASHW_TIMER = 4 // Flash continuously, until the FLASHW_STOP flag is set
FLASHW_TIMERNOFG = 5 // Flash continuously until the window
// comes to the foreground
This should work fine with Windows 98 and above. If you want to work with Windows
95, however, you will need to take a different approach:
FlashWindow
Under Windows 95 (and Delphi versions 3 and below which do not include the appropriate
API wrapper) a different approach is needed. With this operating system the
FlashWindowEx API call does not exist, and you need to use FlashWindow instead. (The
FlashWindow call will work under later operating systems as well.)
The problem with FlashWindow is that it only works once, so to achieve the required
flashing button it is necessary to use a system timer. This, of course, uses up
valuable resources - but it does achieve the desired effect.
For the purposes of this example you need to create a form and drop a Timer and a
Button onto it. Then select the Timer and double click the OnTimer event to create
the event handler. Then add the following code:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
FlashWindow(Application.Handle, True);
end;
Next, you need to start the flashing. To do this simply use the button OnClick
event to set the Enabled property of the Timer to True. In this example I have used
the button event to toggle the flashing on or off:
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled := not Timer1.Enabled;
end;
So there you have it. Two different ways to achieve the same thing.
© Chris Bray /Vertical Software 2002
Update
Within minutes of posting this example Simon Clayton got in touch to suggest a way of
causing the flashing ONLY if the application is not the currently active one. Over
to Simon:
I've done it like this:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if (GetForeGroundWindow()<>Form1.Handle) then
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle;
FWinfo.dwflags := FLASHW_ALL;
FWinfo.ucount := 5;
FWinfo.dwtimeout := 0;
Flashing := True;
FlashWindowEx(FWinfo);
end
else if (Flashing) then
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle;
FWinfo.dwflags := FLASHW_STOP;
FWinfo.ucount := 0;
FWinfo.dwtimeout := 0;
FlashWindowEx(FWinfo);
Flashing := false;
end;
end;
I have also put some code in the Form's onPaint event to stop the flashing:
procedure TForm1.FormPaint(Sender: TObject);
begin
if (Flashing) then
begin
FWinfo.cbSize := 20;
FWinfo.hwnd := Application.Handle;
FWinfo.dwflags := FLASHW_STOP;
FWinfo.ucount := 0;
FWinfo.dwtimeout := 0;
FlashWindowEx(FWinfo);
end;
end;
The only problem I have now to solve is that if I am using an app on my second monitor
and the form is on top on my first monitor then the onPaint event doesn't seem to get
called when I switch back to the application which means that the flashing carries on -
something I've noticed is a problem in MS Instant Messenger sometimes anyway.
Well done Simon - great tip! |
|