对话框控件布局类 (有效抵制闪烁)

头文件

#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

后续继续修改...

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值