使用 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 如下所示;当讨论移动和缩放这些控件时,您应该参考此图。
![[Dlg control IDs - 17K]](https://cloudfront.codeproject.com/wtl/wtldlgresize/dlgids.gif)
控件将按照以下规则移动和缩放:
- 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() 的默认参数,这会导致添加一个大小调整手柄。此外,对话框不能缩小到小于其初始尺寸。对话框启动时的样子如下:
![[Main dialog - 28K] \](https://cloudfront.codeproject.com/wtl/wtldlgresize/dlgsize1.gif)
这是缩放映射,列出了控件的移动和缩放行为。请注意新的宏 - 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()
缩放后的对话框如下:
![[Bigger dialog - 61K]](https://cloudfront.codeproject.com/wtl/wtldlgresize/dlgsize2.gif)
请注意,编辑框更宽,浏览器控件更宽更高。这四个分组按钮的行为有点难以用言语形容,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();
    }
