¿Cómo crear un CDialog redimensionable en MFC?

Tengo que crear una aplicación basada en un diálogo, en lugar del viejo tipo de diseño CFormView. Pero CDialog produce diálogos de tamaño fijo. ¿Cómo puedo crear aplicaciones basadas en diálogos con diálogos redimensionables?

En el archivo de recursos RC si el diálogo tiene este estilo similar a este, será de tamaño fijo:

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

Si el diálogo tiene este estilo, será considerable:

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

Con estas opciones de cuadro de gran tamaño, el diálogo será re-dimensionable, pero aún tendrá que trabajar mucho manejando el mensaje WM_SIZE para administrar el tamaño y el posicionamiento de los controles dentro del cuadro de diálogo.

Además de configurar el estilo en WS_THICKFRAME , también es probable que desee tener un sistema para mover y cambiar el tamaño de los controles en un cuadro de diálogo a WS_THICKFRAME que se WS_THICKFRAME el tamaño del cuadro de diálogo. Para mi uso personal, he creado una clase base para reemplazar el CDialog que tiene esta capacidad. Derive de esta clase y en su función InitDialog , llame a la función AutoMove para cada control secundario para definir cuánto debe moverse y cuánto debe redimensionarse en relación con el diálogo principal. El tamaño del diálogo en el archivo de recursos se usa como tamaño 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); } 

Si usa una plantilla de diálogo, abra la plantilla de diálogo en el editor de recursos y establezca la propiedad Estilo en Emergente y la propiedad Borde en Redimensionar . Estoy bastante seguro de que esto hará lo mismo que lo que dijo jussij y establecerá los estilos WS_POPUP y WS_THICKFRAME. Para configurarlos dinámicamente, anule la función PreCreateWindow y agregue lo siguiente:

 cs.style |= WS_POPUP | WS_THICKFRAME; 

No hay una manera fácil de hacer esto. Básicamente, necesitará controles de diseño dynamics cuando se cambie el tamaño de la ventana.

Consulte http://www.codeproject.com/KB/dialog/resizabledialog.aspx para ver un ejemplo

He probado muchas bibliotecas de diseño MFC y encontré esta la mejor: http://www.codeproject.com/KB/dialog/layoutmgr.aspx . Echa un vistazo a los comentarios de algunas correcciones de errores y mejoras (descargo de responsabilidad: algunos de ellos por mí;)). Cuando use esta biblioteca, la configuración de los indicadores de cambio de tamaño correctos en su ventana se manejará por usted.

Tengo algunas instrucciones de blog sobre cómo crear un diálogo re-considerable muy minimalista en MFC.

Básicamente es una implementación de la publicación de Paulo Messina en CodeProject, pero con la mayor cantidad de elementos extraños eliminados como sea posible, solo para ayudar a aclarar cómo hacerlo mejor.

Es bastante fácil de implementar una vez que has tenido un poco de práctica: los bits importantes son:

yo. asegúrese de tener sus bibliotecas de CodeProject, etc., en su proyecto y todo se comstack correctamente.

ii. realice la inicialización adicional requerida dentro del método OnInitDialog: haga que la pinza sea visible, configure el tamaño máximo de inserción, agregue puntos de anclaje a los elementos de control de diálogo que desea “estirar”, etc.

iii. Reemplace el uso de CDialog con CResizableDialog en los puntos apropiados: en la definición de la clase de diálogo, constructor, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog, etc.

Desde Visual Studio 2015 , puede utilizar el diseño de cuadros de diálogo dynamics de MFC , pero parece que no hay forma de restringir el tamaño del diálogo al tamaño mínimo (solo sigue el método anterior manejando WM_GETMINMAXINFO ).

El diseño dynamic se puede hacer:

  • en el momento del diseño en el editor de recursos seleccionando el control y configurando las propiedades Tipo de desplazamiento y Tipo de tamaño (esto emite la nueva sección AFX_DIALOG_LAYOUT en el archivo .rc);
  • o usando programáticamente la clase CMFCDynamicLayout .

Documentación: Diseño dynamic