使用 WTL 内置的对话框调整大小类






4.96/5 (28投票s)
2001 年 6 月 25 日
5分钟阅读

397081

2727
如何使用 WTL 对可调整大小对话框的内置支持
引言
最近我一直在学习 WTL,偶然发现了一个似乎没有被提及过的相当有趣的类:CDialogResize
。鉴于 MFC 中有大量实现可调整大小对话框的示例,WTL 提供自己的类真是太好了,这样您只需学习一个类和一种指定哪些控件需要调整大小的方法。在本文中,我将概述 WTL 的缩放支持,并提供一个示例程序来说明其中一些功能。您应该已经熟悉 WTL 以及如何安装它。如果您需要这方面的帮助,WTL 部分有相关的文章。
使用 CDialogResize
基础知识
与许多其他 WTL 功能一样,您首先需要将 CDialogResize
添加到对话框类的继承列表中。因此,如果您使用 WTL AppWizard 创建一个基于对话框的应用程序,则需要在红色显示的行中添加此处列出的代码:
class CMainDlg : public CAxDialogImpl<CMainDlg>, public CDialogResize<CMainDlg>
CDialogResize
声明在 atlframe.h 中,因此如果您的头文件中还没有,请添加该头文件。
下一步是在对话框的 OnInitDialog
处理程序中初始化 CDialogResize
代码:
LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // Init the CDialogResize code DlgResize_Init(); ... }
DlgResize_Init()
有几个可选参数,我稍后会介绍。
接下来,在对话框的消息映射中添加一个条目,将缩放消息传递给 CDialogResize
:
BEGIN_MSG_MAP(CMainDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) ... CHAIN_MSG_MAP(CDialogResize<CMainDlg>) END_MSG_MAP()
最后,在对话框类中添加一个新的映射,列出对话框中哪些控件将要被缩放:
class CMainDlg : public CAxDialogImpl<CMainDlg>, public CDialogResize<CMainDlg> { ... public: BEGIN_DLGRESIZE_MAP(CMainDlg) END_DLGRESIZE_MAP() ... };
我将在“设置缩放映射”部分介绍如何填充此映射。
初始化 CDialogResize
CDialogResize
通过调用 DlgResize_Init()
进行初始化。其原型为:
void CDialogResize::DlgResize_Init ( bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_THICKFRAME | WS_CLIPCHILDREN );
参数如下:
bAddGripper
:此参数控制CDialogResize
是否在对话框的右下角添加一个大小调整手柄。传递true
添加大小调整手柄,传递false
则不添加。bUseMinTrackSize
:此参数控制CDialogResize
是否限制对话框的最小尺寸。如果传递true
,则不允许将对话框缩小到小于其初始尺寸(如资源文件中所示)。如果不希望限制对话框的最小尺寸,请传递false
。dwForceStyle
:指定应用于对话框的窗口样式。默认值通常就足够了。
设置缩放映射
对话框缩放映射告诉 CDialogResize
要移动或缩放哪些控件。一个条目的格式如下:
DLGRESIZE_CONTROL(ControlID, Flags)
ControlID
是对话框控件的 ID。可能的标志及其含义如下:
DLSZ_SIZE_X
:当对话框水平缩放时,调整控件的宽度。DLSZ_SIZE_Y
:当对话框垂直缩放时,调整控件的高度。DLSZ_MOVE_X
:当对话框水平缩放时,水平移动控件。DLSZ_MOVE_Y
:当对话框垂直缩放时,垂直移动控件。DLSZ_REPAINT
:每次移动/缩放后使控件无效,以便它每次都重绘。
请注意,您不能在同一维度上移动和缩放控件。例如,如果同时指定 DLSZ_MOVE_X
和 DLSZ_SIZE_X
,则尺寸标志将被忽略。
您还可以将控件分组,使它们能够相对于彼此成比例地移动和缩放。我稍后将介绍此主题。
示例项目
本文附带的示例项目是一个简单的基于对话框的应用程序,它充当浏览器(使用 WebBrowser ActiveX 控件)。控件 ID 如下所示;当讨论移动和缩放这些控件时,您应该参考此图。
控件将按照以下规则移动和缩放:
- Location 编辑框将水平缩放。
- Go、Exit 和 About 按钮将水平移动。
- Back、Forward、Stop 和 Refresh 按钮将作为一个组水平缩放。
- 浏览器控件将水平和垂直缩放。
浏览器控件的 OnInitDialog()
函数如下初始化 CDialogResize
:
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { // Init the CDialogResize code DlgResize_Init(); ... }
这使用了 DlgResize_Init()
的默认参数,这会导致添加一个大小调整手柄。此外,对话框不能缩小到小于其初始尺寸。对话框启动时的样子如下:
这是缩放映射,列出了控件的移动和缩放行为。请注意新的宏 - BEGIN_DLGRESIZE_GROUP()
和 END_DLGRESIZE_GROUP()
- 将四个浏览器控件按钮放入一个缩放组中。
BEGIN_DLGRESIZE_MAP(CMainDlg) // Location edit box DLGRESIZE_CONTROL(IDC_URL, DLSZ_SIZE_X) // Go, Exit, About buttons DLGRESIZE_CONTROL(IDC_GO, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_EXIT, DLSZ_MOVE_X) DLGRESIZE_CONTROL(ID_APP_ABOUT, DLSZ_MOVE_X) // IE control buttons BEGIN_DLGRESIZE_GROUP() DLGRESIZE_CONTROL(IDC_BACK, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_FORWARD, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_STOP, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_REFRESH, DLSZ_SIZE_X) END_DLGRESIZE_GROUP() // WebBrowser control DLGRESIZE_CONTROL(IDC_BROWSER, DLSZ_SIZE_X|DLSZ_SIZE_Y) END_DLGRESIZE_MAP()
缩放后的对话框如下:
请注意,编辑框更宽,浏览器控件更宽更高。这四个分组按钮的行为有点难以用言语形容,WTL 代码几乎没有提供指导,因为注释很少。但基本思想是:想象一个围绕所有四个按钮的边界矩形。该矩形像任何其他控件一样缩放,所有按钮都按比例缩放,以保持在边界矩形内。如果按钮被移动而不是缩放,它们将被放置在彼此之间均匀间隔的位置。请注意,组中的所有控件应具有相同的 DLSZ_*
标志以产生有意义的行为。
CDialogResize 的错误和问题
到目前为止,我只遇到两个问题。一个问题是似乎存在一个偏移一像素的错误,因为第一次缩放对话框时,一些控件会朝着错误的方向偏移一像素。另一个更严重的问题是 WTL AppWizard 中的一个错误,当您将 CDialogResize
添加为对话框的基类时,该错误会暴露出来。AppWizard 生成的显示对话框的代码如下所示:
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { // ... CMainDlg dlgMain; int nRet = dlgMain.DoModal(); _Module.Term(); ::CoUninitialize(); return nRet; }
问题在于 CMainDlg
的析构函数在 _Module.Term()
之后被调用。如果在定义了 _ATL_MIN_CRT
符号的情况下构建 release 版本,这会导致崩溃。解决方案是将 CMainDlg
对象放在一个包含块中:
int nRet;
{
CMainDlg dlgMain;
nRet = dlgMain.DoModal();
}