Como criar um CDialog redimensionável no MFC?

Eu tenho que criar um aplicativo baseado em diálogo, em vez do tipo de design CFormView antigo. Mas CDialog produz diálogos de tamanho fixo. Como posso criar aplicativos baseados em diálogo com diálogos redimensionáveis?

No arquivo de resources do RC, se o diálogo tiver esse estilo semelhante a este, será o tamanho fixo:

 IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU 

Se a checkbox de diálogo tiver esse estilo, ela será dimensionável:

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

Com essas opções de frameworks dimensionáveis, a checkbox de diálogo será redimensionável, mas você ainda precisará fazer muito trabalho lidando com a mensagem WM_SIZE para gerenciar o dimensionamento e o posicionamento dos controles dentro da checkbox de diálogo.

Além de configurar o estilo para WS_THICKFRAME , você provavelmente também desejará ter um sistema para mover e resize os controles em um diálogo enquanto o diálogo é redimensionado. Para meu uso pessoal, criei uma class base para replace o CDialog que possui esse recurso. Derive dessa class e em sua function InitDialog chame a function AutoMove para cada controle filho para definir quanto ele deve mover e quanto ele deve resize em relação à checkbox de diálogo pai. O tamanho do diálogo no arquivo de resources é usado como um tamanho mínimo.

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  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 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_) 

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); } 

Se você estiver usando um modelo de diálogo, abra o modelo de diálogo no editor de resources e defina a propriedade Style como Popup e a propriedade Border como Redimensionamento . Tenho certeza que isso vai fazer o mesmo que jussij disse e definir os estilos WS_POPUP e WS_THICKFRAME. Para configurá-los dinamicamente, substitua a function PreCreateWindow e adicione o seguinte:

 cs.style |= WS_POPUP | WS_THICKFRAME; 

Não há maneira fácil de fazer isso. Basicamente, você precisará dinamicamente os controles de layout quando o tamanho da janela é alterado.

Veja http://www.codeproject.com/KB/dialog/resizabledialog.aspx para um exemplo

Eu tentei muitas bibliotecas de layout do MFC e encontrei este o melhor: http://www.codeproject.com/KB/dialog/layoutmgr.aspx . Confira os comentários lá para algumas correções de bugs e melhorias (disclaimer: alguns deles por mim;)). Quando você usa esta biblioteca, definir os sinalizadores de redimensionamento corretos na sua janela será manipulado para você.

Eu tenho algumas instruções do blog sobre como criar um diálogo redimensionável muito minimalista no MFC.

É basicamente uma implementação da postagem de Paulo Messina no CodeProject, mas com o máximo de material desnecessário removido, apenas para ajudar a esclarecer como fazê-lo melhor.

É bastante simples de implementar uma vez que você tenha um pouco de prática: os bits importantes são:

Eu. Certifique-se de ter suas bibliotecas CodeProject, etc., inseridas em seu projeto e todas elas compiladas corretamente.

ii. faça a boot extra necessária dentro do método OnInitDialog: torne a pinça visível, defina o tamanho máximo de dilatação, adicione pontos de ancoragem aos itens de controle da checkbox de diálogo que você deseja ‘esticar’ etc.

iii. Substitua o uso de CDialog por CResizableDialog nos pontos apropriados: na definição da class de diálogo, construtor, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog etc.

Desde o Visual Studio 2015 , você pode usar o layout de checkbox de diálogo dynamic do MFC , mas parece que não é possível restringir o tamanho de checkbox de diálogo ao tamanho mínimo (ainda apenas a maneira antiga, manipulando WM_GETMINMAXINFO ).

Layout dynamic pode ser feito:

  • em tempo de design no editor de resources, selecionando o controle e definindo as propriedades Moving Type e Sizing Type (isso emite uma nova seção AFX_DIALOG_LAYOUT no arquivo .rc);
  • ou programaticamente usando a class CMFCDynamicLayout .

Documentação: Layout Dinâmico