头文件
#ifndef _SEV_LAYOUT_H_ #define _SEV_LAYOUT_H_ //#define _SEV_SYNC_LAYOUT_ //#define _SEV_NAMESPACE_ #pragma once #ifdef _SEV_NAMESPACE_ namespace SevLayout { #endif /* OnSizing里面的 nSide布局如图 4 3 5 ┌───────┐ │ │ 1 │ │ 2 │ │ └───────┘ 7 6 8 #define FIXSIDE_LEFT 0x01 #define FIXSIDE_RIGHT 0x02 #define FIXSIDE_TOP 0x03 #define FIXSIDE_TOPLEFT 0x04 #define FIXSIDE_TOPRIGHT 0x05 #define FIXSIDE_BOTTOM 0x06 #define FIXSIDE_BOTTOMLEFT 0x07 #define FIXSIDE_BOTTOMRIGHT 0x08 #define FIXSIDE_ALL 0x0F */ /*============================================================================== 控件的位置相对于父窗口的位置固定的边 ==============================================================================*/ #define FIXSIDE_LEFT 0x01 #define FIXSIDE_RIGHT 0x02 #define FIXSIDE_TOP 0x04 #define FIXSIDE_BOTTOM 0x08 #define FIXSIDE_TOPLEFT 0x05 #define FIXSIDE_TOPRIGHT 0x06 #define FIXSIDE_BOTTOMLEFT 0x09 #define FIXSIDE_BOTTOMRIGHT 0x0A #define FIXSIDE_ALL 0x0F #define FIXSIDE_DELTA 0x00 #define FIXSIDE_RATE 0x10 class CSevLayout { public: ~CSevLayout(); void SetOwner(CWnd* pWnd); CSevLayout(){pWnd_=NULL;}; //need set in virtual function OnCreate with "this" pointer. //or define it with "this" pointer in dialog class declaration with below statement. //CSevLayout(CWnd* pWnd){pWnd_=pWnd}; //private: CSevLayout(){}; public: void PosCtrl(UINT nID,UINT nFixOption=(FIXSIDE_TOP|FIXSIDE_LEFT)); void PosCtrl(UINT nID,int nPosRateX,int nPosRateY); void LayCtrl(UINT nID,UINT nFixOption=(FIXSIDE_TOP|FIXSIDE_LEFT),int nZoomRateX=100,int nZoomRateY=100); void LayoutCtrl(UINT nID,int nPosRateX,int nPosRateY,int nZoomRateX,int nZoomRateY); void SetMinRect(CRect rect){minRect_ = rect;}; void LayoutBegin(); void LayoutEnd(); #ifdef _SEV_SYNC_LAYOUT_ //用栈对象,来同步LayoutBegin和LayoutEnd... friend class CSevSyncLayout; #endif //_SEV_SYNC_LAYOUT_ int deltaX_,deltaY_; double RateX_,RateY_; private: CWnd *pWnd_; CRect oldRect_,newRect_,minRect_; }; #ifdef _SEV_SYNC_LAYOUT_ class CSevSyncLayout { CSevSyncLayout(CSevLayout&Layout) { Layout.LayoutBegin(); }; ~CSevSyncLayout() { Layout.LayoutEnd(); }; }; #endif //_SEV_SYNC_LAYOUT_ #ifdef _SEV_NAMESPACE_ } #endif //_SEV_NAMESPACE_ #endif //_SEV_LAYOUT_H_
#include <stdafx.h> #include <assert.h> #include "CSevLayout.h" #ifdef _SEV_NAMESPACE_ namespace SevLayout { #endif /*============================================================================== 函数读取原始的窗口指针,并读取窗口第一个的矩形 ==============================================================================*/ void CSevLayout::SetOwner(CWnd* pWnd) { pWnd_=pWnd; assert(pWnd_); pWnd_->GetWindowRect(&oldRect_); minRect_=CRect(0,0,300,200); }; CSevLayout::~CSevLayout() { }; /*============================================================================== 在OnSize里面开始时需要再次使用:读取新的矩形并计算相关增量和增量比 ==============================================================================*/ void CSevLayout::LayoutBegin() { // ASSERT(!pWnd_); pWnd_->GetWindowRect(&newRect_); // pWnd_->SetRedraw (FALSE); if (newRect_.Width()<minRect_.Width()) newRect_.right = newRect_.left + minRect_.Width(); if (newRect_.Height()<minRect_.Height()) newRect_.bottom = newRect_.top + minRect_.Height(); pWnd_->ValidateRect(newRect_); deltaX_ = newRect_.Width() - oldRect_.Width(); deltaY_ = newRect_.Height() - oldRect_.Height(); #pragma warning(disable:4244) RateX_ = newRect_.Width() / oldRect_.Width(); RateY_ = newRect_.Height() / oldRect_.Height(); #pragma warning(default:4244) } /*============================================================================== 在OnSize里面结束时需要使用:读取新的矩形以便下一次计算时使用 ==============================================================================*/ void CSevLayout::LayoutEnd() { oldRect_ = newRect_; //pWnd_->GetWindowRect(&oldRect_); // deltaX_ = 0; // deltaY_ = 0; // RateX_ = 1; // RateY_ = 1; // pWnd_->SetRedraw (); pWnd_->Invalidate (); pWnd_->UpdateWindow(); } /*============================================================================== 控件布局主要部分 程序先获得当前控件的Rect 然后根据相应的选项移动和缩放控件Rect /Param:nID 控件ID /Param:nFixSideOption 对齐和缩放的相关选项 /Param:nZoomRateX X方向的缩放百分比 100 /Param:nZoomRateY Y方向的缩放百分比 100 功能分成了好几类,看来只能用switch处理了. ==============================================================================*/ void CSevLayout::LayCtrl(UINT nID,UINT nFixSideOption,int nZoomRateX,int nZoomRateY) { if (!pWnd_->GetDlgItem(nID)) return; if (deltaX_==0 && deltaY_==0) return; double dZoomRateX = (double)nZoomRateX/100; double dZoomRateY = (double)nZoomRateY/100; CRect rt; pWnd_->GetDlgItem(nID)->GetWindowRect(&rt); pWnd_->ScreenToClient(&rt); //似乎没有太大作用 // pWnd_->GetDlgItem(nID)->ValidateRect(rt); #pragma warning(disable:4244) assert( nFixSideOption>=0 && nFixSideOption<=0xFF ); switch (nFixSideOption&0x0F) { //( 1.) 四个角对齐 case (FIXSIDE_TOP|FIXSIDE_LEFT)://离左边框与上边框距离不变,其余边按比例变化 //左上角位置坐标不变.(左上角对齐(默认)) //大小(增量变化) rt.bottom += deltaY_*dZoomRateY; //控件下边框 离 窗口下边框 距离根据 deltaY_以及dZoomRateY 按比例变化 rt.right += deltaX_*dZoomRateX; //控件右边框 离 窗口右边框 距离根据 deltaY_以及dZoomRateX 按比例变化 break; case (FIXSIDE_TOP|FIXSIDE_RIGHT)://离上边框与右边框距离不变,其余边按比例变化 //左上角位置向右移动deltaX_.(右上角对齐) rt.right += deltaX_; rt.left += deltaX_; //大小(增量变化) rt.bottom += deltaY_*dZoomRateY; rt.left -= deltaX_*dZoomRateX; break; case (FIXSIDE_BOTTOM|FIXSIDE_LEFT)://离下边框与左边框距离不变,其余边按比例变化 //左上角位置下移deltaY_.(左下角对齐) rt.bottom += deltaY_; rt.top += deltaY_; //大小(增量变化) rt.top -= deltaY_*dZoomRateY; rt.right += deltaX_*dZoomRateX; break; case (FIXSIDE_BOTTOM|FIXSIDE_RIGHT)://离下边框与右边框距离不变,其余边按比例变化 //左上角右移deltaX_,下移deltaY_.(右下角对齐) rt.top += deltaY_; rt.bottom += deltaY_; rt.left += deltaX_; rt.right += deltaX_; //大小(增量变化) rt.top -= deltaY_*dZoomRateY; rt.left -= deltaX_*dZoomRateX; break; //( 2.) 对立边对齐 case FIXSIDE_TOP: //上边框 离 窗口上边框 距离 不变 //下边框 离 窗口上边框 距离 根据nZoomRateY比例变化. rt.bottom *= RateY_*dZoomRateY; //左右根据窗口比例缩放 rt.left *= RateX_*dZoomRateX/2; rt.right *= RateX_*dZoomRateX/2; break; case FIXSIDE_BOTTOM: //下边框 离 窗口下边框 距离 不变 rt.top += deltaY_; rt.bottom += deltaY_; //上边框 离 窗口下边框 距离 根据nZoomRateY比例变化. rt.top *= deltaY_*dZoomRateY; //左右根据窗口比例缩放 rt.left *= RateX_*dZoomRateX/2; rt.right *= RateX_*dZoomRateX/2; break; case FIXSIDE_LEFT: //左边框 离 窗口左边框 距离 不变 //右边框 离 窗口左边框 距离 根据nZoomRateX比例变化. rt.right *= RateX_*dZoomRateX; //上下根据窗口比例缩放 rt.top *= RateY_*dZoomRateY/2; rt.bottom *= RateY_*dZoomRateY/2; break; case FIXSIDE_RIGHT: //右边框 离 窗口右边框距离 不变 rt.left += deltaX_; rt.right += deltaX_; //左边框 离 窗口右边框 距离 根据nZoomRateX比例变化. rt.left *= deltaX_*dZoomRateX; //上下根据窗口比例缩放 rt.top *= RateY_*dZoomRateY/2; rt.bottom *= RateY_*dZoomRateY/2; break; //( 3.) 位置大小一同根据比例缩放 //case FIXSIDE_ALL://四个方向都根据窗口缩放 矩形根据窗口比例缩放 case (FIXSIDE_TOP|FIXSIDE_BOTTOM|FIXSIDE_RIGHT|FIXSIDE_LEFT): rt.left *= RateX_*dZoomRateX/2; rt.right *= RateX_*dZoomRateX/2; rt.top *= RateY_*dZoomRateY/2; rt.bottom *= RateY_*dZoomRateY/2; break; default: assert(1&&(_T("参数错误!"))); } #pragma warning(default:4244) pWnd_->GetDlgItem(nID)->SetRedraw (FALSE); pWnd_->GetDlgItem(nID)->MoveWindow(rt,FALSE); pWnd_->GetDlgItem(nID)->SetRedraw (TRUE); pWnd_->GetDlgItem(nID)->ValidateRect(rt); // pWnd_->GetDlgItem(nID)->Invalidate (); } /*============================================================================== 位置的调整,还算优雅. ==============================================================================*/ void CSevLayout::PosCtrl(UINT nID,UINT nOption/*Left or top will override by right or bottom*/) { if (!pWnd_->GetDlgItem(nID)) return; if (deltaX_==0 && deltaY_==0) return; CRect rt; pWnd_->GetDlgItem(nID)->GetWindowRect(&rt); pWnd_->ScreenToClient(&rt); //nOption &= 0x0C; #pragma warning(disable:4244) rt.top += deltaY_*(nOption&FIXSIDE_BOTTOM? 1:0); rt.bottom += deltaY_*(nOption&FIXSIDE_BOTTOM? 1:0); rt.left += deltaX_*(nOption&FIXSIDE_RIGHT? 1:0); rt.right += deltaX_*(nOption&FIXSIDE_RIGHT? 1:0); #pragma warning(default:4244) pWnd_->GetDlgItem(nID)->SetRedraw (FALSE); pWnd_->GetDlgItem(nID)->MoveWindow(rt,FALSE); pWnd_->GetDlgItem(nID)->SetRedraw (TRUE); pWnd_->GetDlgItem(nID)->ValidateRect(rt); // pWnd_->GetDlgItem(nID)->Invalidate (); } /*============================================================================== 位置的调整,微调也可使用的方式 (在x轴上根据deltaX移动的比率,在y轴上根据deltaY移动的比率) (0,为默认不移动,100为完全移动)(后续可能改成double或者1000的比率) 一般的: ( 0, 0;) 左上对齐 ( 0, 100;) 左下对齐 ( 100, 0;) 右上对齐 ( 100, 100;) 右下对齐 ==============================================================================*/ void CSevLayout::PosCtrl(UINT nID,int nPosRateX,int nPosRateY) { if (!pWnd_->GetDlgItem(nID)) return; if (deltaX_==0 && deltaY_==0) return; CRect rt; pWnd_->GetDlgItem(nID)->GetWindowRect(&rt); pWnd_->ScreenToClient(&rt); #pragma warning(disable:4244) rt.left += deltaX_*nPosRateX/100; rt.right += deltaX_*nPosRateX/100; rt.top += deltaY_*nPosRateY/100; rt.bottom += deltaY_*nPosRateY/100; #pragma warning(default:4244) pWnd_->GetDlgItem(nID)->SetRedraw (FALSE); pWnd_->GetDlgItem(nID)->MoveWindow(rt,FALSE); pWnd_->GetDlgItem(nID)->SetRedraw (TRUE); pWnd_->GetDlgItem(nID)->ValidateRect(rt); // pWnd_->GetDlgItem(nID)->Invalidate (); } /*============================================================================== 复杂的位置和尺寸根据主窗口变化而变化的方式.试着优雅的解决 貌似这是存在冲突的方案,那么优先处理位置还是大小呢? 要不要加入第一个处理对于第二个处理流程的限制呢? 或者大小为0的问题,丢给上一层处理.多加一个函数来限制大小... 如果小于某个值了就不执行转换,如果大于某个值了再执行转换? 放到LayoutBegin里面? ==============================================================================*/ void CSevLayout::LayoutCtrl(UINT nID,int nPosRateX,int nPosRateY,int nZoomRateX,int nZoomRateY) { if (!pWnd_->GetDlgItem(nID)) return; if (deltaX_==0 && deltaY_==0) return; CRect rt; pWnd_->GetDlgItem(nID)->GetWindowRect(&rt); pWnd_->ScreenToClient(&rt); #pragma warning(disable:4244) //先做位置吧... rt.left += deltaX_*nPosRateX/100; rt.right += deltaX_*nPosRateX/100; rt.top += deltaY_*nPosRateY/100; rt.bottom += deltaY_*nPosRateY/100; //做增量的比例变化吧... rt.right += deltaX_*nZoomRateX/100; rt.bottom += deltaY_*nZoomRateY/100; #pragma warning(default:4244) pWnd_->GetDlgItem(nID)->SetRedraw (FALSE); //取消重绘 pWnd_->GetDlgItem(nID)->MoveWindow(rt,FALSE); //移动 pWnd_->GetDlgItem(nID)->SetRedraw (TRUE); //开启重绘 pWnd_->GetDlgItem(nID)->ValidateRect(rt); //强制当前不绘制 // pWnd_->GetDlgItem(nID)->Invalidate (); } #ifdef _SEV_NAMESPACE_ } #endif