头文件
#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_
cpp文件
#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
后续继续修改...