Winapi 如何在MFC中创建可调整大小的CDialog?

Winapi 如何在MFC中创建可调整大小的CDialog?,winapi,mfc,Winapi,Mfc,我必须创建一个基于对话框的应用程序,而不是旧的CFormView类型的设计。但是CDialog会生成固定大小的对话框。如何使用可调整大小的对话框创建基于对话框的应用程序?没有简单的方法可以做到这一点。基本上,当窗口大小更改时,您需要动态布局控件 请参见RC资源文件中的示例,如果对话框的样式与此类似,则其大小将是固定的: IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | W

我必须创建一个基于对话框的应用程序,而不是旧的CFormView类型的设计。但是CDialog会生成固定大小的对话框。如何使用可调整大小的对话框创建基于对话框的应用程序?

没有简单的方法可以做到这一点。基本上,当窗口大小更改时,您需要动态布局控件


请参见RC资源文件中的示例

,如果对话框的样式与此类似,则其大小将是固定的:

IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
如果对话框具有此样式,则其大小将相当大:

IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME

使用这些可调整大小的框架选项,对话框将可重新调整大小,但您仍需要处理WM_SIZE消息以管理对话框中控件的大小和位置。

我尝试了许多MFC布局库,发现这一个是最好的:。查看这里的评论,了解一些bug修复和改进(免责声明:其中一些是我写的;))。使用此库时,将为您在窗口上设置正确的调整大小标志。

如果您使用对话框模板,请在资源编辑器中打开对话框模板,并将Style属性设置为Popup,将Border属性设置为调整大小。我很确定这将和jussij所说的一样,并设置WS_弹出窗口和WS_THICKFRAME样式。要动态设置这些参数,请重写PreCreateWindow函数并添加以下内容:

cs.style |= WS_POPUP | WS_THICKFRAME;

除了将样式设置为
WS_THICKFRAME
,您可能还需要一个系统,以便在调整对话框大小时移动和调整对话框中的控件。为了个人使用,我创建了一个基类来替换具有此功能的CDialog。从该类派生,并在
InitDialog
函数中为每个子控件调用
AutoMove
函数,以定义它应该移动多少以及相对于父对话框应该调整多少大小。资源文件中对话框的大小用作最小大小

BaseDialog.h:

#if !defined(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_)
#define AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <vector>

class CBaseDialog : public CDialog
{
// Construction
public:
    CBaseDialog(UINT nIDTemplate, CWnd* pParent = NULL);   // standard constructor

    void AutoMove(int iID, double dXMovePct, double dYMovePct, double dXSizePct, double dYSizePct);

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CBaseDialog)
protected:
    //}}AFX_VIRTUAL

protected:
    //{{AFX_MSG(CBaseDialog)
    virtual BOOL OnInitDialog();
    afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

public:
    bool            m_bShowGripper;         // ignored if not WS_THICKFRAME

private:
    struct SMovingChild
    {
        HWND        m_hWnd;
        double      m_dXMoveFrac;
        double      m_dYMoveFrac;
        double      m_dXSizeFrac;
        double      m_dYSizeFrac;
        CRect       m_rcInitial;
    };
    typedef std::vector<SMovingChild>   MovingChildren;

    MovingChildren  m_MovingChildren;
    CSize           m_szInitial;
    CSize           m_szMinimum;
    HWND            m_hGripper;
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA__INCLUDED_)
#如果!已定义(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA_包含在内)
#定义AFX\u基本对话框\u H\uu DF4DE489\u 4474\u 4759\u A14E\u EB3FF0CDFBDA\uu包括在内_
#如果MSC版本>1000
#布拉格语一次
#endif/\u MSC\u VER>1000
#包括
类CBaseDialog:公共CDialog
{
//建筑
公众:
CBaseDialog(UINT-nIDTemplate,CWnd*pParent=NULL);//标准构造函数
无效自动移动(int iID、双dXMovePct、双dYMovePct、双dXSizePct、双dYSizePct);
//覆盖
//类向导生成的虚拟函数重写
//{{AFX_虚拟(CBaseDialog)
受保护的:
//}}AFX_虚拟
受保护的:
//{{AFX_MSG(CBaseDialog)
虚拟BOOL-OnInitDialog();
afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR*lpMMI);
afx_msg void OnSize(UINT nType、int cx、int cy);
//}}AFX_味精
声明消息映射()
公众:
bool m_bShowGripper;//如果不是WS_THICKFRAME,则忽略
私人:
结构SMovingChild
{
HWND m_HWND;
双m_dXMoveFrac;
双m_动态压裂;
双m_dXSizeFrac;
双m_dYSizeFrac;
正确的开始;
};
typedef std::向量移动子对象;
移动儿童m_移动儿童;
csizem_szInitial;
csizem_szMinimum;
HWND m_hg裂土器;
};
//{{AFX_INSERT_LOCATION}
//微软Visual C++将在前一行之前插入附加声明。
#endif/!已定义(AFX_BASEDIALOG_H__DF4DE489_4474_4759_A14E_EB3FF0CDFBDA_包含在内)
BaseDialog.cpp:

#include "stdafx.h"
#include "BaseDialog.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

CBaseDialog::CBaseDialog(UINT nIDTemplate, CWnd* pParent /*=NULL*/)
    : CDialog(nIDTemplate, pParent),
      m_bShowGripper(true),
      m_szMinimum(0, 0),
      m_hGripper(NULL)
{
}


BEGIN_MESSAGE_MAP(CBaseDialog, CDialog)
    //{{AFX_MSG_MAP(CBaseDialog)
    ON_WM_GETMINMAXINFO()
    ON_WM_SIZE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CBaseDialog::AutoMove(int iID, double dXMovePct, double dYMovePct, double dXSizePct, double dYSizePct)
{
    ASSERT((dXMovePct + dXSizePct) <= 100.0);   // can't use more than 100% of the resize for the child
    ASSERT((dYMovePct + dYSizePct) <= 100.0);   // can't use more than 100% of the resize for the child
    SMovingChild s;
    GetDlgItem(iID, &s.m_hWnd);
    ASSERT(s.m_hWnd != NULL);
    s.m_dXMoveFrac = dXMovePct / 100.0;
    s.m_dYMoveFrac = dYMovePct / 100.0;
    s.m_dXSizeFrac = dXSizePct / 100.0;
    s.m_dYSizeFrac = dYSizePct / 100.0;
    ::GetWindowRect(s.m_hWnd, &s.m_rcInitial);
    ScreenToClient(s.m_rcInitial);
    m_MovingChildren.push_back(s);
}

BOOL CBaseDialog::OnInitDialog()
{
    CDialog::OnInitDialog();

    // use the initial dialog size as the default minimum
    if ((m_szMinimum.cx == 0) && (m_szMinimum.cy == 0))
    {
        CRect rcWindow;
        GetWindowRect(rcWindow);
        m_szMinimum = rcWindow.Size();
    }

    // keep the initial size of the client area as a baseline for moving/sizing controls
    CRect rcClient;
    GetClientRect(rcClient);
    m_szInitial = rcClient.Size();

    // create a gripper in the bottom-right corner
    if (m_bShowGripper && ((GetStyle() & WS_THICKFRAME) != 0))
    {
        SMovingChild s;
        s.m_rcInitial.SetRect(-GetSystemMetrics(SM_CXVSCROLL), -GetSystemMetrics(SM_CYHSCROLL), 0, 0);
        s.m_rcInitial.OffsetRect(rcClient.BottomRight());
        m_hGripper = CreateWindow(_T("Scrollbar"), _T("size"), WS_CHILD | WS_VISIBLE | SBS_SIZEGRIP,
                                  s.m_rcInitial.left, s.m_rcInitial.top, s.m_rcInitial.Width(), s.m_rcInitial.Height(),
                                  m_hWnd, NULL, AfxGetInstanceHandle(), NULL);
        ASSERT(m_hGripper != NULL);
        if (m_hGripper != NULL)
        {
            s.m_hWnd = m_hGripper;
            s.m_dXMoveFrac = 1.0;
            s.m_dYMoveFrac = 1.0;
            s.m_dXSizeFrac = 0.0;
            s.m_dYSizeFrac = 0.0;
            m_MovingChildren.push_back(s);

            // put the gripper first in the z-order so it paints first and doesn't obscure other controls
            ::SetWindowPos(m_hGripper, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
        }
    }

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CBaseDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
    CDialog::OnGetMinMaxInfo(lpMMI);

    if (lpMMI->ptMinTrackSize.x < m_szMinimum.cx)
        lpMMI->ptMinTrackSize.x = m_szMinimum.cx;
    if (lpMMI->ptMinTrackSize.y < m_szMinimum.cy)
        lpMMI->ptMinTrackSize.y = m_szMinimum.cy;
}

void CBaseDialog::OnSize(UINT nType, int cx, int cy) 
{
    CDialog::OnSize(nType, cx, cy);

    int iXDelta = cx - m_szInitial.cx;
    int iYDelta = cy - m_szInitial.cy;
    HDWP hDefer = NULL;
    for (MovingChildren::iterator p = m_MovingChildren.begin();  p != m_MovingChildren.end();  ++p)
    {
        if (p->m_hWnd != NULL)
        {
            CRect rcNew(p->m_rcInitial);
            rcNew.OffsetRect(int(iXDelta * p->m_dXMoveFrac), int(iYDelta * p->m_dYMoveFrac));
            rcNew.right += int(iXDelta * p->m_dXSizeFrac);
            rcNew.bottom += int(iYDelta * p->m_dYSizeFrac);
            if (hDefer == NULL)
                hDefer = BeginDeferWindowPos(m_MovingChildren.size());
            UINT uFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER;
            if ((p->m_dXSizeFrac != 0.0) || (p->m_dYSizeFrac != 0.0))
                uFlags |= SWP_NOCOPYBITS;
            DeferWindowPos(hDefer, p->m_hWnd, NULL, rcNew.left, rcNew.top, rcNew.Width(), rcNew.Height(), uFlags);
        }
    }
    if (hDefer != NULL)
        EndDeferWindowPos(hDefer);

    if (m_hGripper != NULL)
        ::ShowWindow(m_hGripper, (nType == SIZE_MAXIMIZED) ? SW_HIDE : SW_SHOW);
}
#包括“stdafx.h”
#包括“BaseDialog.h”
#ifdef_调试
#定义新调试\u新
#取消定义此文件
静态字符此\u文件[]=\u文件\u;
#恩迪夫
CBaseDialog::CBaseDialog(UINT-nIDTemplate,CWnd*pParent/*=NULL*/)
:CDialog(nIDTemplate,pParent),
m_b展示夹持器(正确),
m_最小值(0,0),
m_hg裂土器(空)
{
}
开始消息映射(CBASETALOG、CDialog)
//{{AFX_MSG_地图(CBaseDialog)
关于_WM_GETMINMAXINFO()
关于_WM_SIZE()
//}}AFX_MSG_地图
结束消息映射()
void cbase对话框::自动移动(int iID、双dXMovePct、双dYMovePct、双dXSizePct、双dYSizePct)
{
ASSERT((dXMovePct+dXSizePct)我有一些关于如何在MFC中创建一个非常简单的可重新调整大小的对话框的知识

它基本上是一个 但尽可能多地去除无关的东西,只是为了帮助澄清如何做得更好

一旦您进行了一点实践,实施起来就相当简单:重要的是:

i、 确保您的项目中包含了他的代码项目库等,并且所有代码都能正确编译

ii.在OnInitDialog方法中执行所需的额外初始化:使夹持器可见,设置最大dilog大小,向希望“拉伸”的对话框控件项添加锚定点等


iii.在适当的位置用CResizableDialog替换CDDialog的用法:在对话框类定义中,构造函数、DoDataExchange、BEGIN\u MESSAGE\u MAP、OnInitDialog等。

由于Visual Studio 2015可以使用,但似乎无法将对话框大小限制为最小大小(仍然只是通过WM_GETMINMAXINFO使用的旧方法)

可以通过以下方式进行动态布局:

  • 在资源编辑器中的设计时,通过选择控件并设置移动类型大小调整类型属性(这会将新的AFX_对话框_布局部分发送到.rc文件)
  • 或者通过编程方式使用cmfcdynamicclayout

文档:

问题与C++无关。因此,我删除了这个标签。在这个上面可能抛出Win32标签,RC文件的东西是基本的Win32,甚至不是MFC特定的。@ AdvARK: RC的东西可能不是MFC特定的,但是标题指定MFC,<代码> cBudie<代码>(在问题中指定)。