Wie eine veränderbare CDialog in MFC erstellen?
Frage
Ich habe eine dialogbasierte Anwendung zu erstellen, anstelle der alten CFormView Art von Design. Aber CDialog produziert feste Größe Dialoge. Wie kann ich dialogbasierte Anwendungen mit veränderbaren Dialogen erstellen?
Lösung
In der RC-Ressource-Datei, wenn der Dialog diesen Stil ähnlich dazu wird es eine feste Größe werden:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
Wenn der Dialog diesen Stil hat, wird es beträchtlich sein:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
Mit diesen beträchtlichen Rahmenoptionen wird der Dialog erneut beträchtlich sein, aber Sie müssen noch eine Menge Arbeit tun Umgang mit der WM_SIZE Nachricht, die die Dimensionierung eine Positionierung der Kontrollen im Dialog zu verwalten.
Andere Tipps
Neben der Einrichtung, den Stil WS_THICKFRAME
, werden Sie wahrscheinlich wollen auch ein System haben, um die Kontrollen in einem Dialog zu bewegen und die Größe, wie der Dialog der Größe verändert wird. Für meinen eigenen persönlichen Gebrauch habe ich eine Basisklasse erstellt CDialog zu ersetzen, die diese Fähigkeit hat. Man leitet von dieser Klasse und in Ihrer Funktion InitDialog
die AutoMove
Funktion für jedes Kind Rufsteuerung zu definieren, wie viel es bewegen und soll, wie viel es sollte auf den übergeordneten Dialog relative Größe ändern. Die Größe des Dialogs in der Ressource-Datei wird als Mindestgröße verwendet wird.
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);
}
Wenn ein Dialog Vorlage dann öffnen Sie das Dialogvorlage in dem Ressource-Editor und stellen Sie die Style Eigenschaft auf Popup und Border Immobilien Ändern der Größe . Ich bin mir ziemlich sicher, dass dies das gleiche tun wie das, was jussij sagte und legen Sie die WS_POPUP und WS_THICKFRAME Stile. Um überschreiben diese dynamisch dann die PreCreateWindow-Funktion und fügen Sie den folgenden:
cs.style |= WS_POPUP | WS_THICKFRAME;
Es gibt keine einfache Möglichkeit, dies zu tun. Grundsätzlich müssen Sie dynamisch Layout steuert, wenn die Fenstergröße verändert wird.
Siehe http://www.codeproject.com/KB/dialog/resizabledialog. aspx für ein Beispiel
Da Visual Studio 2015 , können Sie Handhabung WM_GETMINMAXINFO ).
Dynamische Layout kann getan werden:
- zur Entwurfszeit in Ressource-Editor, indem Sie die Steuerung der Auswahl und Einstellung der Bewegen Typ und Sizing Typ Eigenschaften (dies emittiert neue AFX_DIALOG_LAYOUT in RC-Datei);
- oder programmatisch die CMFCDynamicLayout mit Klasse .
Dokumentation: Dynamisches Layout
Ich habe viele MFC-Layout-Bibliotheken versucht und fanden, dass dieses eine der besten: http: //www.codeproject.com/KB/dialog/layoutmgr.aspx . Schauen Sie sich die Kommentare dort für einige Fehlerkorrekturen und Verbesserungen (Disclaimer: Einige von ihnen von mir;)). Wenn Sie diese Bibliothek verwenden, die korrekten Resize-Flaggen auf Ihrem Fenster Einstellung wird für Sie behandelt werden.
Ich habe einige Blog Anweisungen , wie man einen sehr minimalistischen Re-beträchtlicher Dialog in MFC erstellen.
Es ist im Grunde eine Implementierung von Paulo Messina Posting bei Codeproject aber mit so viel Fremd Sachen wie möglich entfernt, nur um zu helfen, zu klären, wie es besser zu machen.
Es ist ziemlich einfach zu implementieren, wenn Sie ein bisschen Übung gehabt haben: die wichtigen Bits sind:
i. sicherzustellen, haben Sie seine Codeproject Bibliotheken usw. in das Projekt gezogen und alles kompiliert korrekt.
ii. tun, um die zusätzliche Initialisierung in der OnInitDialog-Methode erforderlich. macht die Greifer sichtbar, die maximale DILOG Größe, Ankerpunkte an die Dialogsteuerung Elemente, die Sie wollen zu ‚strecken‘ usw. hinzufügen
iii. Ersetzen Verwendung von CDialog mit CResizableDialog an den entsprechenden Stellen: im Dialogklassendefinition, Konstruktor, DoDataExchange, BEGIN_MESSAGE_MAP, OnInitDialog usw.