xresizable.cpp

来自「Visual_C++[1].NET_Bible1 Visual_C++宝典书中」· C++ 代码 · 共 322 行

CPP
322
字号
// XResizable.cpp : implementation file
//

#include "stdafx.h"
#include "XResizable.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// a helper function for calculating proportions
static int MulDiv3(int x,int now,int was,int ini) {
	return ::MulDiv(::MulDiv(x,ini,was),now,ini);
}

void CXResizable::ResizeControls() {
	// if not resizable, then don't bother
	if (! IsResizable()) return;
	// get the original, old and new sizes
	SResizeInfo resizeInfo;
	CRect rcWas;
	GetClientRectWas(rcWas);
	CRect rcNow;
	GetClientRectNow(rcNow);
	CRect rcIni;
	GetClientRectIni(rcIni);
	// has it changed?
	if (! rcWas.IsRectEmpty() && ! rcNow.IsRectEmpty() && rcWas != rcNow) {
		// first lets count the child controls so we can
		// preallocatethe DeferWindowPos
		int n = 0;
		HWND hwnd = CWnd_GetSafeHwnd();
		::EnumChildWindows(hwnd,EnumChildProc_CountControls,reinterpret_cast<LPARAM>(&n));
		// pack up the info we need into a struct
		// that we pass to the callback
		resizeInfo.szNow = rcNow.Size();
		resizeInfo.szWas = rcWas.Size();
		resizeInfo.szIni = rcIni.Size();
		resizeInfo.pResizable = this;
		// we defer the movement of controls until after we've worked out all the new sizes
		resizeInfo.defer = ::BeginDeferWindowPos(n);
		ASSERT(resizeInfo.defer);
		// resize all the child controls
		::EnumChildWindows(hwnd,EnumChildProc_ResizeControls,reinterpret_cast<LPARAM>(&resizeInfo));
		ASSERT(resizeInfo.defer);
		// do all the deferred sizing now
		VERIFY(::EndDeferWindowPos(resizeInfo.defer));
		(*this)->Invalidate();
	}
}

BOOL CALLBACK CXResizable::EnumChildProc_CountControls(HWND /*hwnd*/, LPARAM lParam) {
	// just bump up the counter
	int* pCount = reinterpret_cast<int*>(lParam);
	(*pCount)++;
	return TRUE;
}

BOOL CALLBACK CXResizable::EnumChildProc_ResizeControls(HWND hwnd, LPARAM lParam) {
	// the lparam is a pointer to  struct that
	// contains the info we need
	SResizeInfo* pResizeInfo = reinterpret_cast<SResizeInfo*>(lParam);
	// the wndow must be valid
	if (hwnd && ::IsWindow(hwnd)) {
		// get a CWnd* for the hwnd (for convenience)
		CWnd* pControl = CWnd::FromHandle(hwnd);
		// and find out the id number of the control
		int id = pControl->GetDlgCtrlID();
		// only resize if the id number is valid
		if (id) {
			// we are inside a static function, so there
			// is not this pointer.  Instead we pass the
			// this pointer in with the struct, so
			// pResizeable will point to the TXResizable
			// dialog/page that called us
			CXResizable* pResizable = pResizeInfo->pResizable;
			// get the sizes we need including the 
			// current window rectangle for the control
			// (in client coordinates)
			CSize szNow = pResizeInfo->szNow;
			CSize szWas = pResizeInfo->szWas;
			CSize szIni = pResizeInfo->szIni;
			CSize szDelta = szNow - szWas;
			CRect rcControl;
			pControl->GetWindowRect(rcControl);
			CRect rcControlwas(rcControl);
			pResizable->CWnd_ScreenToClient(rcControl);
			// now resize the of the control
			pResizable->ResizeControl(id,pControl,rcControl, szNow,szWas,szIni,szDelta);
			// if the size or position has changed
			if (rcControl != rcControlwas) {
				// defer the repositioning until later
				// we use NOCOPYBITS so that transparent
				// controls like static will resize properly
				pResizeInfo->defer = ::DeferWindowPos(
					pResizeInfo->defer,
					(*pControl),NULL,
					rcControl.left,rcControl.top,rcControl.Width(),rcControl.Height(),
					SWP_DRAWFRAME|SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_NOREPOSITION|SWP_NOZORDER
					);
				ASSERT(pResizeInfo->defer);
			}
		}
	}
	return TRUE;
}

void CXResizable::ResizeControl(UINT id,CWnd* pControl,CRect& rcControl, const CSize& szNow, const CSize& szWas, const CSize& szIni, const CSize& szDelta) {
	// bydefault we use the resize flags to resize
	// we can override this to use other methods if required
	ResizeControlUsingFlags(id,pControl,rcControl, szNow,szWas,szIni,szDelta);
}

void CXResizable::ResizeControlUsingFlags(UINT id,CWnd* pControl,CRect& rcControl, const CSize& szNow, const CSize& szWas, const CSize& szIni, const CSize& szDelta) {
	// first lets ask for the appropriate flag for this control
	UINT nFlags = GetResizeFlags(id,pControl,rcControl, szNow,szWas,szIni,szDelta);
	// now adjust the retangle accordingly
	if (nFlags != RESIZE_NONE) {
		{
			int l = rcControl.left;
			int r = rcControl.right;
			int c = (l+r+1)/2;
			int dlprop = ::MulDiv3(l,szNow.cx,szWas.cx,szIni.cx)-l;
			int drprop = ::MulDiv3(r,szNow.cx,szWas.cx,szIni.cx)-r;
			int dcprop = ::MulDiv3(c,szNow.cx,szWas.cx,szIni.cx)-c;
			int dfull = szDelta.cx;
			switch (nFlags & RESIZE_LEFT_FLAGS) {
			case RESIZE_LEFT_LFIX:
				break;
			case RESIZE_LEFT_LPROP:
				rcControl.left += dlprop;
				break;
			case RESIZE_LEFT_RPROP:
				rcControl.left += drprop;
				break;
			case RESIZE_LEFT_CPROP:
				rcControl.left += dcprop;
				break;
			case RESIZE_LEFT_RFIX:
				rcControl.left += dfull;
				break;
			}
			switch (nFlags & RESIZE_RIGHT_FLAGS) {
			case RESIZE_RIGHT_LFIX:
				break;
			case RESIZE_RIGHT_LPROP:
				rcControl.right += dlprop;
				break;
			case RESIZE_RIGHT_RPROP:
				rcControl.right += drprop;
				break;
			case RESIZE_RIGHT_CPROP:
				rcControl.right += dcprop;
				break;
			case RESIZE_RIGHT_RFIX:
				rcControl.right += dfull;
				break;
			}
		}
		{
			int t = rcControl.top;
			int b = rcControl.bottom;
			int c = (t+b+1)/2;
			int dtprop = ::MulDiv3(t,szNow.cy,szWas.cy,szIni.cy)-t;
			int dbprop = ::MulDiv3(b,szNow.cy,szWas.cy,szIni.cy)-b;
			int dcprop = ::MulDiv3(c,szNow.cy,szWas.cy,szIni.cy)-c;
			int dfull = szDelta.cy;
			switch (nFlags & RESIZE_TOP_FLAGS) {
			case RESIZE_TOP_TFIX:
				break;
			case RESIZE_TOP_TPROP:
				rcControl.top += dtprop;
				break;
			case RESIZE_TOP_BPROP:
				rcControl.top += dbprop;
				break;
			case RESIZE_TOP_CPROP:
				rcControl.top += dcprop;
				break;
			case RESIZE_TOP_BFIX:
				rcControl.top += dfull;
				break;
			}
			switch (nFlags & RESIZE_BOTTOM_FLAGS) {
			case RESIZE_BOTTOM_TFIX:
				break;
			case RESIZE_BOTTOM_TPROP:
				rcControl.bottom += dtprop;
				break;
			case RESIZE_BOTTOM_BPROP:
				rcControl.bottom += dbprop;
				break;
			case RESIZE_BOTTOM_CPROP:
				rcControl.bottom += dcprop;
				break;
			case RESIZE_BOTTOM_BFIX:
				rcControl.bottom += dfull;
				break;
			}
		}
	}
}

UINT CXResizable::GetResizeFlags(UINT id,CWnd* pControl,CRect& rcControl, const CSize& szNow, const CSize& szWas, const CSize& szIni, const CSize& szDelta) {
	// by default we will use the smart resize method to
	// automatically calculate an appropriate resize flag
	return GetSmartResizeFlags(id,pControl,rcControl, szNow,szWas,szIni,szDelta);
}

UINT CXResizable::GetSmartResizeFlags(UINT /*id*/,CWnd* pControl,CRect& rcControl, const CSize& /*szNow*/, const CSize& szWas, const CSize& /*szIni*/, const CSize& /*szDelta*/) {
	// not very smart in this implementation
	return RESIZE_NONE;
}

void CXResizable::GetGripperRect(CRect& rcGripper) {
	// the gripper is at the bottom right and is
	// the same size asa scroll bar
	GetClientRectNow(rcGripper);
	rcGripper.left = rcGripper.right - ::GetSystemMetrics(SM_CXVSCROLL);
	rcGripper.top = rcGripper.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
}

void CXResizable::DrawGripper(CDC* pDC) {
	// draw the gripper in the appropriate position
	CRect rcGripper;
	GetGripperRect(rcGripper);
	pDC->DrawFrameControl(rcGripper, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
}

bool CXResizable::IsResizable() const {
	// is this dialog/page resizeable?
	bool bResizable = true;
	if (::IsWindow(CWnd_GetSafeHwnd())) {
		// only if it doesn't have a modal frame
		bResizable = ((*this)->GetStyle() & DS_MODALFRAME) != DS_MODALFRAME;
	}
	return bResizable;
}

bool CXResizable::IsResizeDirectly() const {
	// property pages cannot be sized directly
	// (the property sheet will resize them instead)
	bool bIsDialog = (*this)->IsKindOf(RUNTIME_CLASS(CDialog));
	bool bIsPropertyPage  = (*this)->IsKindOf(RUNTIME_CLASS(CPropertyPage));
	return bIsDialog && ! bIsPropertyPage;
}

void CXResizable::OnSize(UINT /*nType*/, int /*cx*/, int /*cy*/) {
	if (::IsWindow(CWnd_GetSafeHwnd())) {
		CRect rc, rw;
		(*this)->GetClientRect(rc);
		(*this)->GetWindowRect(rw);
		// do we have a valid rectangle?
		if (! rc.IsRectEmpty()) {
			// copy the previous rectangle into m_rcWas
			// if there was one .. otherwise just set it
			if (m_rcNow.IsRectEmpty()) {
				m_rcWas = rc;
				m_rwWas = rw;
			} else {
				m_rcWas = m_rcNow;
				m_rwWas = m_rwNow;
			}
			m_rcNow = rc;
			m_rwNow = rw;
			// if we haven't already set up the initial
			// rectnagle, do it now
			if (m_rcIni.IsRectEmpty()) {
				m_rcIni = rc;
				m_rwIni = rw;
			}
			// if the size has changed, resize ourselves
			if (m_rcNow != m_rcWas) {
				ResizeControls();
			}
		}
	}	
}

UINT CXResizable::OnNcHitTest(CPoint point) {
	if (::IsWindow(CWnd_GetSafeHwnd()) && IsResizeDirectly()) {
		if (IsResizable() && ! (*this)->IsZoomed()) {
			// check for a hit in the gripper
			CPoint pointScreen = point;
			CWnd_ScreenToClient(&pointScreen);
			CRect rcGripper;
			GetGripperRect(rcGripper);
			if (rcGripper.PtInRect(pointScreen)) {
				return HTBOTTOMRIGHT;
			}
		}
	}
	return HTNOWHERE;
}

bool CXResizable::OnPaint() {
	if (::IsWindow(CWnd_GetSafeHwnd()) && IsResizable()) {
		CPaintDC dc(*this); // device context for painting
		CRect rc;
		GetClientRectNow(rc);
		dc.FillSolidRect(rc,::GetSysColor(COLOR_3DFACE));
		// draw the gripper if required
		if (IsResizeDirectly() && ! (*this)->IsZoomed()) {
			DrawGripper(&dc);
			return true;
		}
	}
	return false;
}

void CXResizable::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) {
	if (::IsWindow(CWnd_GetSafeHwnd())) {
		// the smallest size is the original dialog/page size
		CRect rw;
		GetWindowRectIni(rw);
		lpMMI->ptMinTrackSize.x = rw.Width();
		lpMMI->ptMinTrackSize.y = rw.Height();
	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?