Introduction
First, let me show you some screenshots captured from the demo program.
The program demonstrates semi-transparent dialogs which are compatible with Windows 2000 and higher.
Background
Layered windows, supported from Windows NT 5.0, provide a way to create windows that have a complex shape with alpha blending effects. The major challenge is how to show standard controls on layered windows.
The following shows the mechanism:
When the dialog is being created, a fake window is created by CreateWindowEx
with the styles WS_EX_LAYERED
, WS_EX_TRANSPARENT
, and WS_EX_NOACTIVATE
. The alpha value of the real window will be modified to 5 by SetLayeredWindowAttributes
so that the real window is almost transparent.
The real window is in charge of processing user input events and Windows messages; the fake one is in charge of the presentation. The fake window is always kept the same size / position as the real one.
How do we show standard controls on the fake window? When the presentation needs to be refreshed, the background image is painted first. Then, all the 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.
When should we refresh the presentation? We need to refresh the fake window when there is an update on the UI. In the demo, it hooks into all the child controls recursively, and changes the WNDPROC
address by SetWindowLongPtr
with the parameter GWLP_WNDPROC
.
You can get more details by looking into the source code.
Using the Code in Native C++ / MFC
First Step
Copy all the files in /Src/*.* to your project.
Second Step
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.
Final Step
Replace the dialog base class from CDialog
to CImgDialogBase
.
CDemo2Dlg::CDemo2Dlg(CWnd* pParent )
: CImgDialogBase( CDemo2Dlg::IDD
, CUtility::GetModulePath() + _T("background.png")
, pParent
)
{
}
CDemo3Dlg::CDemo3Dlg(CWnd* pParent )
: CImgDialogBase(CDemo3Dlg::IDD
, IDB_PNG_DLG2
, _T("PNG")
, AfxGetResourceHandle()
, pParent
)
{
}
Using the Code in WinForms/.NET
First Step
Copy files in the /Src/*.* directory to your project.
Second Step
You need an image file to be the dialog background. You'd better choose PNG or TIFF which support alpha channel.
Final Step
Replace the dialog base class from Form
to ImageDlgBase
.
public partial class Form2 : CoolImageDlg.ImageDlgBase
{
public Form2()
{
base.DlgBgImg = ImgDlgSample.Properties.Resources.DemoDlgBg2;
}
}
Something Important
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.
Some of the controls cannot work with WM_PRINT
; in that case, the control won't display correctly. In other words, not all controls are supported.
The sample code uses the GDI+ helper class from Zoltan Csizmadia. For those who do not want to use GDIPlus.dll, CxImage is another choice.
Most of the machines use 96 DPI as their monitor setting. For those machines which use an unusual DPI setting, please note the demo is not meant for such settings and the child controls will be misplaced. You need to add your own code to re-layout the child controls if you do need to support unusual DPIs.