我必须创建一个基于对话框的应用程序,而不是旧的CFormView类型的设计.但是CDialog会生成固定大小的对话框.如何使用可调整大小的对话框创建基于对话框的应用程序?
在RC资源文件中,如果对话框具有与此类似的样式,则它将是固定大小:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
如果对话框具有此样式,则它将是相当大的:
IDD_DIALOG_DIALOG DIALOGEX 0, 0, 320, 201 STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
使用这些相当大的框架选项,对话框将重新调整大小,但您仍需要执行大量处理WM_SIZE消息的工作来管理对话框中控件的定位大小.
除了将样式设置为WS_THICKFRAME
,您可能还希望在调整对话框大小时让系统在对话框中移动和调整控件的大小.为了个人用途,我创建了一个基类来替换具有此功能的CDialog.从这个类派生,并在你的InitDialog
函数调用AutoMove
每个子控件定义多少应该移动,多少它应该调整相对父对话框功能.资源文件中对话框的大小用作最小大小.
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 #includeclass 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); }