Je dois créer une application basée sur un dialogue, au lieu de l'ancien type de conception CFormView. Mais CDialog produit des boîtes de dialogue de taille fixe. Comment créer des applications basées sur des dialogues avec des dialogues redimensionnables?
Dans le fichier de ressources RC, si le style de la boîte de dialogue est similaire à celui-ci, sa taille sera fixe:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
Si la boîte de dialogue a ce style, elle sera importante:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
Avec ces options de cadres dimensionnables, la boîte de dialogue sera redimensionnable, mais vous devrez néanmoins effectuer beaucoup de travail pour gérer le message WM_SIZE afin de gérer le dimensionnement et le positionnement des commandes dans la boîte de dialogue.
En plus de définir le style sur WS_THICKFRAME
, vous souhaiterez probablement également disposer d'un système pour déplacer et redimensionner les contrôles dans une boîte de dialogue lorsque celle-ci est redimensionnée. Pour mon usage personnel, j'ai créé une classe de base pour remplacer CDialog qui offre cette possibilité. Dérivez de cette classe et, dans votre fonction InitDialog
, appelez la fonction AutoMove
pour chaque contrôle enfant afin de définir le déplacement et le redimensionnement par rapport au dialogue parent. La taille de la boîte de dialogue dans le fichier de ressources est utilisée comme taille minimale.
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_)
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 vous utilisez un modèle de dialogue, ouvrez-le dans l’éditeur de ressources et définissez la propriété Style sur Popup et la propriété Border sur Redimensionnement . Je suis à peu près sûr que cela fera la même chose que ce que jussij dit et définit les styles WS_POPUP et WS_THICKFRAME. Pour les définir dynamiquement, remplacez la fonction PreCreateWindow et ajoutez ce qui suit:
cs.style |= WS_POPUP | WS_THICKFRAME;
Il n'y a pas de moyen facile de faire cela. Fondamentalement, vous devrez disposer de manière dynamique les contrôles lorsque la taille de la fenêtre est modifiée.
Voir http://www.codeproject.com/KB/dialog/resizabledialog.aspx pour un exemple.
J'ai quelques instructions de blog sur la façon de créer un dialogue très minimaliste et redimensionnable dans MFC.
Il s’agit en fait d’une implémentation de de l’affichage de Paulo Messina dans CodeProject mais avec le plus possible d’éléments superflus supprimés, juste pour aider à clarifier la façon de mieux le faire.
Il est assez simple à mettre en œuvre une fois que vous avez eu un peu de pratique: les éléments importants sont les suivants:
je. Assurez-vous que ses bibliothèques CodeProject, etc., sont bien insérées dans votre projet et que tout est compilé correctement.
ii. effectuez l'initialisation supplémentaire requise dans la méthode OnInitDialog: rendez visible le préhenseur, définissez la taille maximale du dilog, ajoutez des points d'ancrage aux éléments de contrôle de la boîte de dialogue que vous souhaitez "étirer", etc.
iii. Remplacez l'utilisation de CDialog par CResizableDialog aux points appropriés: dans la définition de classe de dialogue, le constructeur, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog, etc.
J'ai essayé de nombreuses bibliothèques de structures MFC et j'ai trouvé celle-ci la meilleure: http://www.codeproject.com/KB/dialog/layoutmgr.aspx . Consultez les commentaires pour des corrections de bugs et des améliorations (disclaimer: certains d’entre eux par moi;)). Lorsque vous utilisez cette bibliothèque, la définition des indicateurs de redimensionnement corrects sur votre fenêtre sera gérée pour vous.
Depuis Visual Studio 2015, vous pouvez utiliser MFC Dynamic Dialog Layout , mais il semble qu’il n’existe aucun moyen de limiter la taille de la boîte de dialogue à une taille minimale (toujours à l’ancienne méthode de traitementWM_GETMINMAXINFO).
La mise en page dynamique peut être faite:
Documentation: Disposition dynamique