65.9K
CodeProject 正在变化。 阅读更多。
Home

可调整大小的 MFC 对话框的帮助类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (8投票s)

2009年3月19日

CPOL

3分钟阅读

viewsIcon

43138

downloadIcon

1028

当您调整 MFC 对话框大小时,此类负责子窗口的放置。它还将大小存储在注册表中以供下次调用。

引言

大家好,这是我在这里的第一篇文章,请多多包涵。

本文介绍了一种在 MFC 应用程序中处理可调整大小对话框的非常简单的方法,使用 Visual Studio 6 到 2005(我还不了解 2008 或更新版本)。

背景

我喜欢 MFC。信不信由你,我喜欢。但是,我不喜欢你必须小心翼翼地对待可调整大小的对话框。你必须手动将每个子窗口的位置编程到你编写的每个对话框的 OnSize 方法中。所以,我花了一个下午的时间研究如何简化这个问题。

因此,根据我对 CDialog 的了解,我可以在对话框的 OnInitialUpdate() 方法中获取当前窗口大小(你在资源编辑器中设置的大小)。存储它,这样下次对话框大小调整时,你可以在 OnGetMinMaxInfo() 方法中确保它不会比这小。

void Init(CDialog* pDialog);

很简单,明白了吧。你只需要传递对话框的“this”指针。

我还可以获取子窗口的当前位置,以便稍后可以使用这些信息来保持与左/右边框和顶部/底部边框的距离,当对话框大小调整时。

默认情况下,如果你不移动它们,所有子窗口都会自动粘在其左上角。但是,如果你想让它们移动,可以使用此函数让调整器为你移动子窗口。

void AddCtrl(int id, int align=DT_LEFT|DT_TOP, 
             BOOL keep_dx=TRUE, BOOL keep_dy=TRUE);

对于对齐标志,我选择了 DrawText API 函数的常量,因为它们(与 TextOut API 的 TA_ 常量相反)有一个 DT_VCENTER 常量。所以,是的,你也可以让子窗口居中对齐对话框。

keep_dx”和“keep_dy”标志告诉大小调整器你是否允许拉伸该子窗口,或者保持其大小。“按钮”通常希望保持大小,“主编辑控件”很可能希望拉伸。

这里完成了

// ---------------------------------------------------
// OnSize handling of the Child Ctrls
// ---------------------------------------------------
void GFResizeDialogHelper::OnSize(int cx, int cy)
{
    m_CurSize.cx = cx;
    m_CurSize.cy = cy;

    for(int i=0; i<(int)m_Ctrls.size(); ++i)
    {
        CTRL_ALIGN& ctrl = m_Ctrls[i];

        int x = ctrl.place.left;
        int width = ctrl.place.Width();
        if(!ctrl.keep_dx)
        {
            width += (cx - m_OrigSize.cx);
            // new size increment = dialog increment
        }
        if(ctrl.align & DT_RIGHT)
        {
            x = ctrl.place.right - width + cx-m_OrigSize.cx;
        }
        else if(ctrl.align & DT_CENTER)
        {
            // ctrl center
            int xcenter = (ctrl.place.left+ctrl.place.right)/2;
            // dialog offset
            xcenter += (cx-m_OrigSize.cx)/2;
            x = xcenter-width/2;
        }
        
        int y = ctrl.place.top;
        int height = ctrl.place.Height();
        if(!ctrl.keep_dy)
        {
            height+=(cy-m_OrigSize.cy);
        }
        if(ctrl.align & DT_BOTTOM)
        {
            // y += cy-m_OrigSize.cy;
            y = ctrl.place.bottom - height + cy-m_OrigSize.cy;
        }
        else if(ctrl.align & DT_VCENTER)
        {
            // ctrl center
            int ycenter = (ctrl.place.top + ctrl.place.bottom)/2;
            // dialog offset
            ycenter += (cy-m_OrigSize.cy)/2;
            y = ycenter - height/2;
        }
        width = max(width, 10);
        height = max(height, 10);
        x = max(0,x);
        y = max(0,y);

        m_pDialog->GetDlgItem(ctrl.idWnd)->MoveWindow(x,y,width, height, FALSE);
        // ::MoveWindow(ctrl.pWnd->GetSafeHwnd(), x,y,width, height, TRUE);
    }
    if(m_pDialog && m_pDialog->GetSafeHwnd() && 
                ::IsWindow(m_pDialog->GetSafeHwnd()))
        m_pDialog->RedrawWindow();
}

接下来,我发现每个对话框都有一个 IDD 常量,这是对话框的资源 ID。很好!我想,我们可以使用这个 ID 在关闭时将对话框的大小存储在注册表中,但我后来发现:m_pDialog->GetRuntimeClass()->m_lpszClassName 返回一个可读的字符串作为对话框类名,我使用了它。

使用代码

代码使用起来非常简单。只需在项目中包含上面两个文件即可。然后,打开 mydialog.h 文件并添加这两行。

include "gfresizehelper.h" // include header

// and add a member variable to your CDialog derived class:
GFResizeDialogHelper    m_Resizer;

下一步是告知助手你的子窗口以及你希望它们在调整大小时如何表现。

BOOL CMyDialog::OnInitDialog()
{
    // ...
    m_Resizer.Init(this);
    m_Resizer.AddCtrl(IDOK, DT_BOTTOM|DT_RIGHT);
    // ... more controls
    // optionally - read and write position and size to registy
    m_Resizer.ReadWriteToRegistry();
}
/*
// Usage of AddCtrl(int id, int align, BOOL keep_dx, BOOL keep_dy)
int id:
    The ID of the control (IDC_...)
int align:
    The alignment of the control. If you align it
    Horizontally:
        DT_LEFT,    the left-coordinate will stay where it is.
        DT_CENTER,    the center of the control will stay where it was,
                    relative to the dialog.
        DT_RIGHT,    the right border of the control will keep the distance
                    to the right border of the dialog.
    Vertically:
        DT_TOP,        the top position will stay
        DT_VCENTER,    the vertical center will stay where it was relative
                    to the dialog.
        DT_BOTTOM,    the controll will keep the distance to the bottom of
                    the dialog
BOOL keep_dx
    Should the control keep its width?
BOOL keep_dy
    Should the control keep its height?
*/

调用 ReadWriteToRegistry() 将尝试从注册表中获取上次存储的大小和位置,并在类析构时存储最后一个 WM_SIZE 消息的大小,这通常发生在 OnOK()OnCancel() 之后。

然后,使用应用程序向导或对话框属性,为 OnSizeWM_SIZE 消息)获取一个消息处理程序,并告诉大小调整助手处理你的子窗口。

void CMyDialog::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);
    m_Resizer.OnSize(cx,cy);
}

最后,你可以处理 WM_GETMINMAXINFO 消息,以告知程序不要将窗口缩小到你设计的尺寸,而不是在对话框编辑器中设计的尺寸。

void CMyDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
    m_Resizer.GetMinMaxInfo(lpMMI);
}

我希望这篇文章能对某人有所帮助。我现在经常使用这个类,我真的很喜欢我的程序的可调整大小的对话框带来的新外观。

© . All rights reserved.