Introduction
First, let me show you some screenshots captured from the demo program. The program demonstrates semi-transparent dialogs with standard, ActiveX (like WebBrowser Control, Flash Control) and translucent controls which are compatible with Windows 2000 and higher.
Background
Jerry.Wang has given a way in his article: Cool, Semi-transparent and Shaped Dialogs with Standard Controls for Windows 2000 and Above. In short, he creates two dialogs: one (the real dialog) is in charge of processing user input events and Windows messages, the other (the fake dialog) is in charge of the presentation. The fake one is created by CreateWindowEx
with the styles WS_EX_LAYERED
, WS_EX_TRANSPARENT
, and WS_EX_NOACTIVATE
, and always kept the same size / position as the real one. The real one is almost transparent because its alpha value is modified to 5 by SetLayeredWindowAttributes
.
In his article, when the presentation needs to be refreshed, the background image is painted first. All the standard child controls will be captured by sending the WM_PRINT
message, and painted at the same position on the fake window. Especially, for EDIT
controls, EditBox
/ Editable ComboBox
/ etc., we need to draw the caret by ourselves.
But, there are some problems:
- The dialog is NOT a real shaped one, i.e. the hit testing is NOT based on the shape and transparency of the dialog. This means that the areas of the dialog that are color-keyed or whose alpha value is zero will NOT let the mouse messages through.
- NOT All standard controls are supported. Some of the controls such as slider, cannot work with
WM_PRINT
; in that case, the controls won't display correctly.
- The ActiveX control is NOT supported. The ActiveX control like the WebBrowser control and Flash control is used to support a wide variety of OLE functionality and can be customized to fit many software needs. It cannot work with
WM_PRINT
too.
- The dialog works in a way that if there is one pixel that needs to be updated, the whole window will be refreshed. Therefore, if the dialog is very large and complex, or has a lot of child controls, it may cause performance issues.
I'm going to propose another approach which can solve the above problems. The following picture shows the mechanism:
The translucent dialog is created with the styles WS_EX_LAYERED
, and WS_OVERLAPPED
. When it is being created, a standard dialog is created by CreateWindowEx
with the styles WS_EX_LAYERED
, WS_POPUP
, NO WS_BORDER
, NO WS_SIZEBOX
, and NO WS_DLGFRAME
.
The translucent dialog is in charge of processing the translucent background and the translucent controls; the standard dialog is in charge of processing the standard controls and ActiveX controls. The standard dialog is always kept the same size / position as the translucent one.
A translucent control which supports the per-pixel alpha value must be derived from the class CTranslucentWnd
. You can override
the method Render
to draw it. In the source code, I has provided two translucent controls: CTranslucentButton
and CTranslucentProgressBar
. The CTranslucentButton
is used to replace the CBitmapButton
, and CTranslucentProgressBar
is used to replace the CProgressCtrl
.
For the standard controls and Active controls, you must put them on the standard dialog. Because the standard dialog covers the translucent dialog, the translucent controls cannot process the user input. How to solve it? The solution is making the standard dialog fully transparent. The API SetLayeredWindowAttributes
with the zero alpha value can help you. But all the controls on it are also fully transparent.
But how to make the standard dialog fully transparent while the controls on it are opaque? This can be done by calling SetLayeredWindowAttributes
with the color-key. In the OnInitDialog
of the standard dialog, the API SetLayeredWindowAttributes
is called with a certain color. In the processing of the message WM_CTLCOLOR
with nCtlColor == CTLCOLOR_DLG
, if it returns the solid brush that has the specified color, the standard dialog is fully transparent while the controls on it are also opaque.
You can get more details by looking into the source code.
Using the Code in Native C++ / MFC
Step 1
Copy all the files in /Core/*.* to your project.
Step 2
You need an image file to be the dialog background. You'd better choose PNG or TIFF which support the alpha channel. The image file can be embedded into a resource or placed on disk, judged by yourself.
Step 3
Design your standard dialog in the Resource View. In the Properties window, set the following properties:
Border: None
Overlapped Window: False
Style: Popup
You'd better choose a certain color (i.e. transparent color) as the color-key, and drag some standard controls or ActiveX controls to it. Replace the dialog base class from CDialog
to CStandardDialog
.
CDemoStandardDlg::CDemoStandardDlg(CWnd* pParent )
: CStandardDialog(CDemoStandardDlg::IDD, RGB(0, 255, 0), pParent)
{
}
Step 4
Design your translucent dialog in the Resource View. In the Properties window, set the following properties:
Overlapped Window: False
Style: Overlapped
Replace the dialog base class from CDialog
to CTranslucentDialog
.
CDemoTranslucentDlg::CDemoTranslucentDlg(LPCTSTR lpszFile, CWnd* pParent )
: CTranslucentDialog(CDemoTranslucentDlg::IDD, lpszFile, pParent)
{
}
CDemoTranslucentDlg::CDemoTranslucentDlg(UINT nImgID, LPCTSTR lpszType
, HINSTANCE hResourceModule, CWnd* pParent )
: CTranslucentDialog(CDemoTranslucentDlg::IDD, nImgID,
lpszType, hResourceModule, pParent)
{
}
Step 5 (optional)
If you need some translucent controls, drag some controls (like button, checkbox, progress bar) to the translucent dialog. Subclasses your translucent controls to the corresponding CTranslucentWnd
class, like subclass CButton
to CTranslucentButton
. The following demo source is from the demo program:
CTranslucentButton m_btnTest;
CTranslucentProgressBar m_ctrlProgress;
void CDemoTranslucentDlg::DoDataExchange(CDataExchange* pDX)
{
CTranslucentDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_BUTTON1, m_btnTest);
DDX_Control(pDX, IDC_PROGRESS, m_ctrlProgress);
}
Step 6
Override the method CreateStandardDialog
and OnInitChildrenWnds
of your translucent dialog class. The method CreateStandardDialog
is in charge of creating the corresponding standard dialog, and the method OnInitChildrenWnds
is in charge of setting the translucent controls' properties and registering them to the translucent dialog. The following demo source is from the demo program:
CStandardDialog* CDemoTranslucentDlg::CreateStandardDialog()
{
return ::new CDemoStandardDlg(this);
}
void CDemoTranslucentDlg::OnInitChildrenWnds()
{
LPCTSTR szImageList[TWS_BUTTON_NUM] = { _T("res\\close_normal.PNG"),
_T("res\\close_disable.png"), _T("res\\close_over.PNG"),
_T("res\\close_down.PNG"), 0};
m_btnTest.LoadImageList(szImageList);
RegisterTranslucentWnd(&m_btnTest);
m_ctrlProgress.MoveWindow(400, 400, 146, 61, TRUE);
m_ctrlProgress.SetPos(50);
m_ctrlProgress.SetFgImage(_T("res\\progress.png"));
RegisterTranslucentWnd(&m_ctrlProgress);
}
Something Important
The article is based on the Jerry.Wang's article, and uses his CUtility
class to load image. Thank you very much, Jerry.Wang.
The source code uses the GdiPlus to draw the translucent controls, so you must setup the Gdiplus environment before using my code.
The source code only provides two kinds of translucent controls. You can extend the CTranslucentWnd
to support some other controls. But in my own projects, they are enough. If you can provide some other translucent controls, please share them with me.
If some controls on the standard dialog have some pixels whose colors are the same as the transparent color of the dialog, the pixels are also fully transparent. You can make use of the feature to create some effects, like making some controls become irregular.