Come creare un CDialog ridimensionabile in MFC?

Devo creare un’applicazione basata su una finestra di dialogo, invece del vecchio tipo di progettazione CFormView. Ma CDialog produce windows di dialogo a dimensione fissa. Come posso creare applicazioni basate su windows di dialogo ridimensionabili?

Nel file di risorse RC se la finestra di dialogo ha questo stile simile a questo, verrà fissata la dimensione:

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

Se la finestra di dialogo ha questo stile sarà considerevole:

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

Con queste opzioni di frame considerevoli la finestra di dialogo sarà ridimensionabile ma sarà comunque necessario fare molto lavoro gestendo il messaggio WM_SIZE per gestire il dimensionamento di un posizionamento dei controlli all’interno della finestra di dialogo.

Oltre a impostare lo stile su WS_THICKFRAME , probabilmente vorrai avere un sistema per spostare e ridimensionare i controlli in una finestra di dialogo mentre la finestra di dialogo viene ridimensionata. Per il mio uso personale ho creato una class base per sostituire CDialog che ha questa capacità. Derivati ​​da questa class e nella tua funzione InitDialog chiama la funzione AutoMove per ogni controllo figlio per definire quanto deve essere spostato e quanto deve ridimensionare rispetto alla finestra di dialogo principale. La dimensione della finestra di dialogo nel file di risorse viene utilizzata come dimensione minima.

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 si utilizza un modello di finestra di dialogo, aprire il modello di finestra di dialogo nell’editor delle risorse e impostare la proprietà Stile su Popup e la proprietà Border su Ridimensionamento . Sono abbastanza sicuro che questo farà lo stesso di quello che ha detto jussij e impostare gli stili WS_POPUP e WS_THICKFRAME. Per impostarli dynamicmente, ignorare la funzione PreCreateWindow e aggiungere quanto segue:

 cs.style |= WS_POPUP | WS_THICKFRAME; 

Non c’è un modo semplice per farlo. Fondamentalmente, sarà necessario disporre dynamicmente i controlli di layout quando si modificano le dimensioni della finestra.

Vedi http://www.codeproject.com/KB/dialog/resizabledialog.aspx per un esempio

Ho provato molte librerie di layout MFC e ho trovato questo il migliore: http://www.codeproject.com/KB/dialog/layoutmgr.aspx . Controlla i commenti lì per alcune correzioni di bug e miglioramenti (disclaimer: alcuni di loro da me;)). Quando si utilizza questa libreria, l’impostazione dei flag di ridimensionamento corretti sulla finestra verrà gestita automaticamente.

Ho alcune istruzioni per il blog su come creare una finestra di dialogo ridimensionabile molto minimalista in MFC.

Si tratta fondamentalmente di un’implementazione del post di Paulo Messina su CodeProject, ma con il maggior numero ansible di cose estranee rimosse, solo per aiutare a chiarire come farlo meglio.

È abbastanza semplice implementare una volta che hai avuto un po ‘di pratica: i bit importanti sono:

io. assicurati di avere le sue librerie CodeProject ecc. inserite nel tuo progetto e tutto verrà compilato correttamente.

ii. eseguire l’inizializzazione aggiuntiva richiesta all’interno del metodo OnInitDialog: rendere visibile il dispositivo di presa, impostare la dimensione massima di dilog, aggiungere punti di ancoraggio agli elementi di controllo della finestra di dialogo che si desidera “allungare” ecc.

iii. Sostituire l’utilizzo di CDialog con CResizableDialog nei punti appropriati: nella definizione della class di dialogo, costruttore, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog, ecc.

A partire da Visual Studio 2015 , è ansible utilizzare il layout di windows di dialogo dinamico MFC , ma sembra che non vi sia alcun modo per limitare le dimensioni della finestra di dialogo a dimensioni minime (ancora solo alla vecchia maniera gestendo WM_GETMINMAXINFO ).

Il layout dinamico può essere fatto:

  • in fase di progettazione nell’editor delle risorse, selezionando il controllo e impostando le proprietà Tipo di spostamento e Tipo di ridimensionamento (questo emette la nuova sezione AFX_DIALOG_LAYOUT nel file .rc);
  • o programmaticamente utilizzando la class CMFCDynamicLayout .

Documentazione: layout dinamico