使用标准、ActiveX 和半透明控件在 Windows 2000+ 上实现完美的半透明和异形对话框
本文旨在寻找一种在分层窗口上显示标准控件、ActiveX 控件和半透明控件的方法。提供了原生的 MFC 源代码。
介绍
首先,我向您展示一些从演示程序中捕获的屏幕截图。该程序演示了具有标准、ActiveX(如 WebBrowser 控件、Flash 控件)和半透明控件的半透明对话框,这些控件兼容 Windows 2000 及更高版本。
背景
Jerry.Wang 在他的文章中提供了一种方法:《酷炫、半透明和异形对话框与标准控件,适用于 Windows 2000 及以上版本》。简而言之,他创建了两个对话框:一个(真实对话框)负责处理用户输入事件和 Windows 消息,另一个(虚拟对话框)负责呈现。虚拟对话框通过 CreateWindowEx
创建,样式为 WS_EX_LAYERED
、WS_EX_TRANSPARENT
和 WS_EX_NOACTIVATE
,并始终保持与真实对话框相同的大小/位置。真实对话框几乎是透明的,因为它的 alpha 值通过 SetLayeredWindowAttributes
修改为 5。
在他的文章中,当需要刷新显示时,首先绘制背景图像。所有标准子控件都通过发送 WM_PRINT
消息捕获,并在虚拟窗口的相同位置绘制。特别是对于 EDIT
控件,EditBox
/ 可编辑 ComboBox
/ 等,我们需要自己绘制插入符。
但是,存在一些问题
- 该对话框不是真正的异形对话框,即点击测试不基于对话框的形状和透明度。这意味着对话框中被颜色键控或 alpha 值为零的区域不会让鼠标消息通过。
- 并非所有标准控件都受支持。某些控件,例如滑块,无法与
WM_PRINT
配合使用;在这种情况下,控件将无法正确显示。 - 不支持 ActiveX 控件。WebBrowser 控件和 Flash 控件等 ActiveX 控件用于支持各种 OLE 功能,并且可以定制以满足许多软件需求。它也无法与
WM_PRINT
配合使用。 - 对话框的工作方式是,如果有一个像素需要更新,整个窗口都会刷新。因此,如果对话框非常大且复杂,或者有很多子控件,可能会导致性能问题。
我将提出另一种可以解决上述问题的方法。下图显示了其机制

半透明对话框使用 WS_EX_LAYERED
和 WS_OVERLAPPED
样式创建。当它被创建时,一个标准对话框通过 CreateWindowEx
创建,样式为 WS_EX_LAYERED
, WS_POPUP
,无 WS_BORDER
,无 WS_SIZEBOX
,和无 WS_DLGFRAME
。
半透明对话框负责处理半透明背景和半透明控件;标准对话框负责处理标准控件和 ActiveX 控件。标准对话框始终保持与半透明对话框相同的大小/位置。
支持逐像素 alpha 值的半透明控件必须派生自 CTranslucentWnd
类。您可以重写 Render
方法来绘制它。在源代码中,我提供了两个半透明控件:CTranslucentButton
和 CTranslucentProgressBar
。CTranslucentButton
用于替换 CBitmapButton
,CTranslucentProgressBar
用于替换 CProgressCtrl
。
对于标准控件和 Active 控件,您必须将它们放在标准对话框上。因为标准对话框覆盖了半透明对话框,所以半透明控件无法处理用户输入。如何解决?解决方案是使标准对话框完全透明。零 alpha 值的 API SetLayeredWindowAttributes
可以帮助您。但它上面的所有控件也都完全透明。
但是如何使标准对话框完全透明,而其上的控件不透明呢?这可以通过使用颜色键调用 SetLayeredWindowAttributes
来完成。在标准对话框的 OnInitDialog
中,调用 API SetLayeredWindowAttributes
并指定某种颜色。在处理 WM_CTLCOLOR
消息时,如果 nCtlColor == CTLCOLOR_DLG
,并且它返回具有指定颜色的实心画刷,则标准对话框将完全透明,而其上的控件也将保持不透明。
您可以查看源代码以获取更多详细信息。
在原生 C++ / MFC 中使用代码
步骤 1
将 /Core/*.* 中的所有文件复制到您的项目中。
第二步
您需要一个图像文件作为对话框背景。您最好选择支持 alpha 通道的 PNG 或 TIFF 格式。图像文件可以嵌入到资源中,也可以放在磁盘上,由您自行决定。
步骤 3
在资源视图中设计您的标准对话框。在属性窗口中,设置以下属性
Border: None
Overlapped Window: False
Style: Popup
您最好选择一种特定的颜色(即透明色)作为颜色键,并拖放一些标准控件或 ActiveX 控件到其中。将对话框基类从 CDialog
替换为 CStandardDialog
。
// Here, the transparent color is green.
CDemoStandardDlg::CDemoStandardDlg(CWnd* pParent /*=NULL*/)
: CStandardDialog(CDemoStandardDlg::IDD, RGB(0, 255, 0), pParent)
{
}
步骤 4
在资源视图中设计您的半透明对话框。在属性窗口中,设置以下属性
Overlapped Window: False
Style: Overlapped
将对话框基类从 CDialog
替换为 CTranslucentDialog
。
// Load from disk file
CDemoTranslucentDlg::CDemoTranslucentDlg(LPCTSTR lpszFile, CWnd* pParent /*=NULL*/)
: CTranslucentDialog(CDemoTranslucentDlg::IDD, lpszFile, pParent)
{
}
// Load from resource
CDemoTranslucentDlg::CDemoTranslucentDlg(UINT nImgID, LPCTSTR lpszType
/*=_T("PNG")*/, HINSTANCE hResourceModule/*=NULL*/, CWnd* pParent /*=NULL*/)
: CTranslucentDialog(CDemoTranslucentDlg::IDD, nImgID,
lpszType, hResourceModule, pParent)
{
}
步骤 5 (可选)
如果您需要一些半透明控件,将一些控件(如按钮、复选框、进度条)拖到半透明对话框中。将您的半透明控件子类化为相应的 CTranslucentWnd
类,例如将 CButton
子类化为 CTranslucentButton
。以下演示源代码来自演示程序
// In the CDemoTranslucentDlg.h
CTranslucentButton m_btnTest;
CTranslucentProgressBar m_ctrlProgress;
// In the CDemoTranslucentDlg.cpp
void CDemoTranslucentDlg::DoDataExchange(CDataExchange* pDX)
{
CTranslucentDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_BUTTON1, m_btnTest);
DDX_Control(pDX, IDC_PROGRESS, m_ctrlProgress);
}
步骤 6
重写您的半透明对话框类的 CreateStandardDialog
和 OnInitChildrenWnds
方法。CreateStandardDialog
方法负责创建相应的标准对话框,OnInitChildrenWnds
方法负责设置半透明控件的属性并将它们注册到半透明对话框。以下演示源代码来自演示程序
// In the CDemoTranslucentDlg.cpp
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);
}
重要事项
本文基于 Jerry.Wang 的文章,并使用他的 CUtility
类加载图像。非常感谢,Jerry.Wang。
源代码使用 GdiPlus 绘制半透明控件,因此在使用我的代码之前必须设置 Gdiplus 环境。
源代码仅提供了两种半透明控件。您可以扩展 CTranslucentWnd
以支持其他控件。但在我自己的项目中,它们已经足够了。如果您能提供其他半透明控件,请与我分享。
如果标准对话框上的一些控件的某些像素颜色与对话框的透明色相同,则这些像素也将完全透明。您可以利用此功能创建一些效果,例如使某些控件变得不规则。