65.9K
CodeProject is changing. Read more.
Home

Producing translucent dialog boxes and windows without flickering

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (11 votes)

Aug 7, 2002

viewsIcon

135630

downloadIcon

3109

Enhancing window fade-in to render common controls correctly and to allow the window to remain transparent after the fade-in

Problem

The AnimateWindow() API with the AW_BLEND parameter is supposed to fade in and out windows smoothly. Basically, it has two drawbacks:
  1. Text and ListView controls (and several others) aren't rendered correctly during fade-in.
  2. You cannot specify a translucency for the window, i.e., after the animation is over, the window is opaque.
Clicking 'No' in the sample project demonstrates both problems.

Workaround?

There seemed to be a simple workaround for this: Adding the WS_EX_LAYERED extended style to the window and calling SetLayeredWindowAttributes() with the desired alpha value, as described in the MSDN Library and in various other sources. Unfortunately, this didn't work either, at least on my XP Pro system. The code produced a very nasty flickering: the window area was initially black.

Solution

The following code shows how to fade in a dialog to a predefinded translucency without flickering:
INT_PTR CALLBACK DialogProc(
  HWND hwndDlg,  // handle to dialog box
  UINT uMsg,     // message
  WPARAM wParam, // first message parameter
  LPARAM lParam  // second message parameter
  ) 
{
   RECT rcDesktop;
   RECT rcMe;

   BYTE bTranslucency;

   const DWORD ANIMATION_MILLIS = 200;
   const BYTE TRANSLUCENCY = 192;
   const BYTE TRANSLUCENCY_STEP = 16;
   const DWORD TRANSLUCENCY_TIMEOUT 
      = TRANSLUCENCY_STEP * ANIMATION_MILLIS / TRANSLUCENCY;

   switch (uMsg) {
   case WM_INITDIALOG:
      // Make it a layered window.
      ::SetWindowLong(hwndDlg, GWL_EXSTYLE,
         ::GetWindowLong(hwndDlg, GWL_EXSTYLE) | WS_EX_LAYERED);

      // Completely transparent window - note the third parameter
      ::SetLayeredWindowAttributes(hwndDlg, 0, 0, LWA_ALPHA);

      // Show it _first_
      ::ShowWindow(hwndDlg, SW_SHOW);

      // Redraw contents NOW - no flickering since the window's not visible
      ::RedrawWindow(hwndDlg, NULL, NULL, RDW_UPDATENOW); 

      // Normally, you would use a timer here...
      for (bTranslucency = 0; bTranslucency < TRANSLUCENCY;
      bTranslucency+=TRANSLUCENCY_STEP) {
         // Adjust the translucency
         ::SetLayeredWindowAttributes(hwndDlg, 0, bTranslucency, LWA_ALPHA);

         // Wait
         ::Sleep(TRANSLUCENCY_TIMEOUT);
      }

      // Set the final translucency
      ::SetLayeredWindowAttributes(hwndDlg, 0, bTranslucency, LWA_ALPHA);
      break;
   }

   return 0;
}
Note that the call to ShowWindow() is usually done by the CreateDialogXXX() functions if the dialog template has the WS_VISIBLE style set. For our purposes, we have to do it right here in response to the WM_INITDIALOG message.