为 Windows 2000 及以上版本打造的炫酷、半透明且异形对话框及标准控件






4.85/5 (88投票s)
本文尝试寻找一种在分层窗口上显示 Windows 标准控件的方法。提供了原生 MFC 和 WinForms 的源代码。
- 下载 .NET 演示 (WinForms) - 280 KB
- 下载 .NET 源代码 (WinForms) - 175 KB
- 下载原生 C++ 演示 (MFC VS2008) - 1.27 MB
- 下载原生 C++ 源代码 (MFC VS2008) - 299 KB
- 下载原生 C++ 源代码 (MFC VC6.0) - 1.04 MB
引言
首先,让我向您展示一些从演示程序截取的屏幕截图。
该程序演示了与 **Windows 2000 及更高版本兼容** 的半透明对话框。
背景
分层窗口(从 Windows NT 5.0 开始支持)提供了一种创建具有复杂形状和 Alpha 混合效果的窗口的方法。主要的挑战是如何在分层窗口上显示标准控件。
以下显示了工作机制
在创建对话框时,通过 CreateWindowEx
创建一个带有 WS_EX_LAYERED
、WS_EX_TRANSPARENT
和 WS_EX_NOACTIVATE
样式的假窗口。通过 SetLayeredWindowAttributes
将真实窗口的 Alpha 值修改为 5,使其几乎透明。
真实窗口负责处理用户输入事件和 Windows 消息;假窗口负责呈现。假窗口始终保持与真实窗口相同的大小/位置。
我们如何在假窗口上显示标准控件?当需要刷新显示时,首先绘制背景图像。然后,通过发送 WM_PRINT
消息捕获所有子控件,并在假窗口的相同位置绘制它们。特别是对于 EDIT 控件、EditBox/Editable ComboBox 等,我们需要自己绘制插入符号。
我们何时应该刷新显示?当 UI 有更新时,我们需要刷新假窗口。在演示中,它递归地钩住所有子控件,并通过 SetWindowLongPtr
和 GWLP_WNDPROC
参数更改 WNDPROC
地址。
您可以通过查看源代码获得更多详细信息。
在原生 C++/MFC 中使用代码
第一步
将 /Src/*.* 中的所有文件复制到您的项目中。
第二步
您需要一个图像文件作为对话框背景。最好选择支持 Alpha 通道的 PNG 或 TIFF。图像文件可以嵌入到资源中,也可以放在磁盘上,由您自己决定。
最后一步
将对话框基类从 CDialog
替换为 CImgDialogBase
。
// Load from disk file
CDemo2Dlg::CDemo2Dlg(CWnd* pParent /*=NULL*/)
: CImgDialogBase( CDemo2Dlg::IDD
, CUtility::GetModulePath() + _T("background.png")
, pParent
)
{
}
// Or load from resource
CDemo3Dlg::CDemo3Dlg(CWnd* pParent /*=NULL*/)
: CImgDialogBase(CDemo3Dlg::IDD
, IDB_PNG_DLG2
, _T("PNG")
, AfxGetResourceHandle()
, pParent
)
{
}
在 WinForms/.NET 中使用代码
第一步
将 /Src/*.* 目录中的文件复制到您的项目中。
第二步
您需要一个图像文件作为对话框背景。最好选择支持 Alpha 通道的 PNG 或 TIFF。
最后一步
将对话框基类从 Form
替换为 ImageDlgBase
。
public partial class Form2 : CoolImageDlg.ImageDlgBase
{
public Form2()
{
base.DlgBgImg = ImgDlgSample.Properties.Resources.DemoDlgBg2;
//......
}
}
重要提示
该对话框的工作方式是,如果有一个像素需要更新,则整个窗口都会被刷新。因此,如果对话框非常大且复杂,或者有很多子控件,可能会导致性能问题。
某些控件无法与 WM_PRINT
一起使用;在这种情况下,控件将无法正确显示。换句话说,并非所有控件都受支持。
示例代码使用了来自 Zoltan Csizmadia 的 GDI+ 辅助类。对于不想使用 GDIPlus.dll 的用户,CxImage 是另一个选择。

大多数计算机的显示器设置为 96 DPI。对于那些使用不寻常 DPI 设置的计算机,请注意,该演示不适用于此类设置,子控件将错位。如果您确实需要支持不寻常的 DPI,则需要添加自己的代码来重新布局子控件。