MFC对话框最大化/窗口化及控件自适应缩放以及嵌套窗口缩放

1. 实现方法(计算窗口放大/缩小比例,控件相应缩放并改变位置,但会存在一定问题)

先在窗口类的头文件中添加相应变量与函数(其中OnSize由类向导消息WM_SIZE生成): 

// XXXDlg.h


// 窗口放大所需变量
private:
	BOOL m_IsInitialized;
	int m_nOldCx, m_nOldCy; // 变量需要在类的构造函数中初始化
public:
	// 改变窗口大小
	void ReSize(int cx, int cy);
	afx_msg void OnSize(UINT nType, int cx, int cy);

实现文件中函数定义为如下:

// XXXDlg.cpp


BOOL CXXXDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// TODO:  在此添加额外的初始化

	// 窗口初始化大小保存
	// 保存窗口初始大小
	CRect m_windowedRect;
	GetClientRect(&m_windowedRect);
	m_nOldCx = m_windowedRect.Width();
	m_nOldCy = m_windowedRect.Height();
	// 初始化完毕flag
	m_IsInitialized = TRUE;

	return TRUE;  // return TRUE unless you set the focus to a control
	// 异常: OCX 属性页应返回 FALSE
}


// 改变窗口大小
void CXXXDlg::ReSize(int cx, int cy)
{
	//计算窗口比率
	float fspx = (float)cx / m_nOldCx;
	float fspy = (float)cy / m_nOldCy;

	CRect rect; 

	//获取子窗口(控件)
	CWnd * pWnd = this->GetWindow(GW_CHILD);
	while(pWnd != NULL)
	{
		pWnd->GetWindowRect(&rect);
		this->ScreenToClient(&rect);
		//重新计算控件位置和大小
		int nNewx = (int)(rect.left * fspx);
		int nNewy = (int)(rect.top * fspy);
		int nNewWidth = (int)(rect.Width() * fspx);
		int nNewHeight = (int)(rect.Height() * fspy);
		//调整控件
		pWnd->MoveWindow(nNewx, nNewy, nNewWidth, nNewHeight);

		//获取下一个子窗口(控件)
		pWnd = pWnd->GetWindow(GW_HWNDNEXT);
	}
}

void CXXXDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码

	if (m_IsInitialized && (nType==SIZE_RESTORED || nType==SIZE_MAXIMIZED))
	{
		ReSize(cx, cy);
		m_nOldCx = cx;
		m_nOldCy = cy;
		Invalidate();//更新窗口 
	}
}

但是这种方法存在一个问题,在来回缩放多次后,由于double转int的精度丢失导致控件会逐渐变小直至消失,如下图:

窗口缩放前控件大小

                              

多次缩放后控件变小

 

下面采用一种改进的方法来解决这个问题。 

解决方案:在第一次放大和第一次缩小的时候分别使用两个vector容器来遍历记录所有控件的位置和大小信息,下面贴代码。

首先定义一个控件位置的结构体:

typedef struct ControlPos
{
	int Top;
	int Left;
	int CtrlWidth;
	int CtrlHeight;
} ControlPos;

然后在需要进行自适应缩放窗口的头文件中添加如下变量:

// XXXDlg.h

#include <vector>

// 窗口放大所需变量
private:
	BOOL m_IsInitialized;
	int m_nOldCx, m_nOldCy;
	int m_maxedCx, m_maxedCy;
	bool m_IsMaxed;
	// 窗口化控件位置
	std::vector<ControlPos> m_vecWindowedControl;
	// 最大化时控件位置
	std::vector<ControlPos> m_vecMaxedControl;
public:
	// 改变窗口大小
	void ReSize(int cx, int cy);
	afx_msg void OnSize(UINT nType, int cx, int cy);

相应的需要在构造函数中进行初始化:

// XXXDlg.cpp


CXXXDlg::CXXXDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CXXXDlg::IDD, pParent)
	, m_nOldCx(0)
	, m_nOldCy(0)
	, m_IsInitialized(false)
	, m_maxedCx(0)
	, m_maxedCy(0)
	, m_IsMaxed(false)
{

}

然后下面是实现函数(OnSize函数是在类向导中的WM_SIZE消息添加的):

// XXXDlg.cpp


// 改变窗口大小
void CDownViewDlg::ReSize(int cx, int cy)
{
	//计算窗口比率
	float fspx = (float)cx / m_nOldCx;
	float fspy = (float)cy / m_nOldCy;

	CRect rect; 

	//获取子窗口(控件)
	CWnd * pWnd = this->GetWindow(GW_CHILD);
	while(pWnd != NULL)
	{
		pWnd->GetWindowRect(&rect);
		this->ScreenToClient(&rect);
		//重新计算控件位置和大小
		int nNewx = (int)(rect.left * fspx);
		int nNewy = (int)(rect.top * fspy);
		int nNewWidth = (int)(rect.Width() * fspx);
		int nNewHeight = (int)(rect.Height() * fspy);

		// 信息赋给结构体
		ControlPos windowedCtrl, maxedCtrl;
		windowedCtrl.Left = rect.left;
		windowedCtrl.Top = rect.top;
		windowedCtrl.CtrlWidth = rect.Width();
		windowedCtrl.CtrlHeight = rect.Height();
		maxedCtrl.Left = nNewx;
		maxedCtrl.Top = nNewy;
		maxedCtrl.CtrlWidth = nNewWidth;
		maxedCtrl.CtrlHeight = nNewHeight;
		// 位置信息加入新旧容器
		m_vecWindowedControl.push_back(windowedCtrl);
		m_vecMaxedControl.push_back(maxedCtrl);
		//调整控件
		pWnd->MoveWindow(nNewx, nNewy, nNewWidth, nNewHeight);

		//获取下一个子窗口(控件)
		pWnd = pWnd->GetWindow(GW_HWNDNEXT);
	}
}

void CDownViewDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码

	if (m_IsInitialized && (nType==SIZE_RESTORED || nType==SIZE_MAXIMIZED))
	{
		if (!m_IsMaxed)
		{
			m_maxedCx = cx;
			m_maxedCy = cy;
			ReSize(cx, cy);
			Invalidate();//更新窗口 
#ifdef _CAM
			close_window(m_lowHWindowID);
			close_window(m_highHWindowID);
			OpenWindow();
			OnBnClickedResetlowimage();
			OnBnClickedResethighimage();
#endif
			m_IsMaxed = true;
		}
		else
		{
			if (cx == m_maxedCx)
			{
				CRect rect; 
				int i = 0; //当做迭代器,把每个控件位置取出

				//获取子窗口(控件)
				CWnd * pWnd = this->GetWindow(GW_CHILD);
				while(pWnd != NULL)
				{
					pWnd->GetWindowRect(&rect);
					this->ScreenToClient(&rect);

					//调整控件
					pWnd->MoveWindow(m_vecMaxedControl[i].Left, m_vecMaxedControl[i].Top, m_vecMaxedControl[i].CtrlWidth, m_vecMaxedControl[i].CtrlHeight);
					i+=1; // 迭代器后移

					//获取下一个子窗口(控件)
					pWnd = pWnd->GetWindow(GW_HWNDNEXT);
				}
#ifdef _CAM
				close_window(m_lowHWindowID);
				close_window(m_highHWindowID);
				OpenWindow();
				OnBnClickedResetlowimage();
				OnBnClickedResethighimage();
#endif
			}
			else
			{
				CRect rect; 
				int i = 0; //当做迭代器,把每个控件位置取出

				//获取子窗口(控件)
				CWnd * pWnd = this->GetWindow(GW_CHILD);
				while(pWnd != NULL)
				{
					pWnd->GetWindowRect(&rect);
					this->ScreenToClient(&rect);

					//调整控件
					pWnd->MoveWindow(m_vecWindowedControl[i].Left, m_vecWindowedControl[i].Top, m_vecWindowedControl[i].CtrlWidth, m_vecWindowedControl[i].CtrlHeight);
					i+=1; // 迭代器后移

					//获取下一个子窗口(控件)
					pWnd = pWnd->GetWindow(GW_HWNDNEXT);
				}
			}
		}
	}
}

这样在后续的放大/缩小中就不会因为精度的丢失而造成控件消失的情况了!

 

2. 升级版

最近在帮师兄做这个页面缩放的时候遇到了问题,由于师兄的页面是由一个主页面(主页面内只有Tab控件)加上两个选项卡页面构成的,所以在代码中先对主页面进行缩放,然后再对下属页面进行上述方法的缩放(下属页面会在主页面调用切换时自动调用OnSize函数进行缩放,具体可加断点查看),具体实现较为复杂,下面贴出代码供参考(还存在一个小BUG):

(这段代码仅供参考,因为有很多与缩放不相关的函数没有删掉)

// XXXDlg.cpp
// 这个cpp是主页面(选项卡页面)的实现文件
// 其余部分与上述方法都一样



// 头文件定义
private:
	// 对话框切换Tab控件
	CTabCtrl m_Tab;
	CRect m_old;
	bool m_isFirstOpen;
	bool m_isFirstMax;
	bool m_isFirstWindow;
	// 窗口化控件位置
	std::vector<ControlPos> m_vecWindowedControl;
	// 最大化时控件位置
	std::vector<ControlPos> m_vecMaxedControl;
public:
        afx_msg void OnSize(UINT nType, int cx, int cy);
	void ReSize(UINT nType, int cx, int cy);



// 构造函数
CDropViewDlg::CDropViewDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CDropViewDlg::IDD, pParent)
	, m_isFirstOpen(false)
	, m_isFirstMax(false)
	, m_isFirstWindow(false)
{

}




void CDropViewDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO: 在此处添加消息处理程序代码
	if (nType == 1)
	{
		return;
	}
	if (nType == 0)//第一次启动对话框时的大小变化不做处理  
	{  
		if (!m_isFirstOpen)
		{
			m_isFirstOpen = true;
			return;
		}
		else
		{
			if (!m_isFirstWindow)
			{
				ReSize(nType, cx, cy);
				CRect TabRect;
				m_Tab.GetClientRect(&TabRect);
				TabRect.left+=1;
				TabRect.right-=1;
				TabRect.top+=25;
				TabRect.bottom-=1;
				m_DlgView.OnSize(nType,cx,cy);
				switch (m_Tab.GetCurSel())   
				{   
				case 0:   
					m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
					m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
					break;   
				case 1:   
					m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
					m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
					break;   
				default:   
					break;   
				}   
				CRect rect; 
				m_DlgView.m_ListTrigger.GetClientRect(&rect);  
				m_DlgView.m_ListTrigger.SetExtendedStyle(m_DlgView.m_ListTrigger.GetExtendedStyle() | LVS_EX_GRIDLINES |LVS_EX_FULLROWSELECT); 
				m_DlgView.m_ListTrigger.InsertColumn(0, _T("参数"), LVCFMT_CENTER, rect.Width()/3, 0);   
				m_DlgView.m_ListTrigger.InsertColumn(1, _T("采集1"), LVCFMT_CENTER, rect.Width()/3, 1);   
				m_DlgView.m_ListTrigger.InsertColumn(2, _T("采集2"), LVCFMT_CENTER, rect.Width()/3, 2); 
				
				Invalidate();//更新窗口 
				m_isFirstWindow = true;
			}
			else
			{
				CRect rect; 
				int i = 0; //当做迭代器,把每个控件位置取出

				//获取子窗口(控件)
				CWnd * pWnd = this->GetWindow(GW_CHILD);
				while(pWnd != NULL)
				{
					pWnd->GetWindowRect(&rect);
					this->ScreenToClient(&rect);

					//调整控件
					pWnd->MoveWindow(m_vecWindowedControl[i].Left,m_vecWindowedControl[i].Top, m_vecWindowedControl[i].CtrlWidth, m_vecWindowedControl[i].CtrlHeight);
					i+=1; // 迭代器后移

					//获取下一个子窗口(控件)
					pWnd = pWnd->GetWindow(GW_HWNDNEXT);
				}
				CRect TabRect;
				m_Tab.GetClientRect(&TabRect);
				TabRect.left+=1;
				TabRect.right-=1;
				TabRect.top+=25;
				TabRect.bottom-=1;
				//m_DlgView.OnSize(nType,cx,cy);
				//m_DlgResult.OnSize(nType,cx,cy);
				switch (m_Tab.GetCurSel())   
				{   
				case 0:   
					m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
					m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
					break;   
				case 1:   
					m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
					m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
					break;   
				default:   
					break;   
				}   
				//CRect rect; 
				m_DlgView.m_ListTrigger.GetClientRect(&rect);  
				m_DlgView.m_ListTrigger.SetExtendedStyle(m_DlgView.m_ListTrigger.GetExtendedStyle() | LVS_EX_GRIDLINES |LVS_EX_FULLROWSELECT); 
				m_DlgView.m_ListTrigger.InsertColumn(0, _T("参数"), LVCFMT_CENTER, rect.Width()/3, 0);   
				m_DlgView.m_ListTrigger.InsertColumn(1, _T("采集1"), LVCFMT_CENTER, rect.Width()/3, 1);   
				m_DlgView.m_ListTrigger.InsertColumn(2, _T("采集2"), LVCFMT_CENTER, rect.Width()/3, 2); 
				
				Invalidate();//更新窗口 
			}
		}
		// 缩小时候ntype==0,这里在第一次打开窗口时候也会进入,所以要放置一个变量判断是否第一次打开窗口
	}  
	else  
	{  
		if (!m_isFirstMax)
		{

			ReSize(nType, cx, cy);
			CRect TabRect;
			m_Tab.GetClientRect(&TabRect);
			TabRect.left+=1;
			TabRect.right-=1;
			TabRect.top+=25;
			TabRect.bottom-=1;
			//m_DlgView.OnSize(nType,cx,cy);
			switch (m_Tab.GetCurSel())   
			{   
			case 0:   
				m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
				m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
				break;   
			case 1:   
				m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
				m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
				break;   
			default:   
				break;   
			}   
			CRect rect; 
			m_DlgView.m_ListTrigger.GetClientRect(&rect);  
			m_DlgView.m_ListTrigger.SetExtendedStyle(m_DlgView.m_ListTrigger.GetExtendedStyle() | LVS_EX_GRIDLINES |LVS_EX_FULLROWSELECT); 
			m_DlgView.m_ListTrigger.InsertColumn(0, _T("参数"), LVCFMT_CENTER, rect.Width()/3, 0);   
			m_DlgView.m_ListTrigger.InsertColumn(1, _T("采集1"), LVCFMT_CENTER, rect.Width()/3, 1);   
			m_DlgView.m_ListTrigger.InsertColumn(2, _T("采集2"), LVCFMT_CENTER, rect.Width()/3, 2); 
			Invalidate();//更新窗口 
			m_isFirstMax = true;
		}
		else
		{
			CRect rect; 
			int i = 0; //当做迭代器,把每个控件位置取出

			//获取子窗口(控件)
			CWnd * pWnd = this->GetWindow(GW_CHILD);
			while(pWnd != NULL)
			{
				pWnd->GetWindowRect(&rect);
				this->ScreenToClient(&rect);

				//调整控件
				pWnd->MoveWindow(m_vecMaxedControl[i].Left, m_vecMaxedControl[i].Top, m_vecMaxedControl[i].CtrlWidth, m_vecMaxedControl[i].CtrlHeight);
				i+=1; // 迭代器后移

				//获取下一个子窗口(控件)
				pWnd = pWnd->GetWindow(GW_HWNDNEXT);
			}
			CRect TabRect;
			m_Tab.GetClientRect(&TabRect);
			TabRect.left+=1;
			TabRect.right-=1;
			TabRect.top+=25;
			TabRect.bottom-=1;
			//m_DlgView.OnSize(nType,cx,cy);
			//m_DlgResult.OnSize(nType,cx,cy);
			switch (m_Tab.GetCurSel())   
			{   
			case 0:   
				m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
				m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
				break;   
			case 1:   
				m_DlgView.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_HIDEWINDOW);
				m_DlgResult.SetWindowPos(NULL,TabRect.left,TabRect.top,TabRect.Width(),TabRect.Height(),SWP_SHOWWINDOW);
				break;   
			default:   
				break;   
			}   
			//CRect rect; 
			m_DlgView.m_ListTrigger.GetClientRect(&rect);  
			m_DlgView.m_ListTrigger.SetExtendedStyle(m_DlgView.m_ListTrigger.GetExtendedStyle() | LVS_EX_GRIDLINES |LVS_EX_FULLROWSELECT); 
			m_DlgView.m_ListTrigger.InsertColumn(0, _T("参数"), LVCFMT_CENTER, rect.Width()/3, 0);   
			m_DlgView.m_ListTrigger.InsertColumn(1, _T("采集1"), LVCFMT_CENTER, rect.Width()/3, 1);   
			m_DlgView.m_ListTrigger.InsertColumn(2, _T("采集2"), LVCFMT_CENTER, rect.Width()/3, 2); 			
			Invalidate();//更新窗口 
		}
	}
}


void CDropViewDlg::ReSize(UINT nType, int cx, int cy)
{
	float fspx = (float)cx / m_old.Width();
	float fspy = (float)cy / m_old.Height();

	CRect rect; 

	//获取子窗口(控件)
	CWnd * pWnd = this->GetWindow(GW_CHILD);
	while(pWnd != NULL)
	{
		pWnd->GetWindowRect(&rect);
		this->ScreenToClient(&rect);
		//重新计算控件位置和大小
		int nNewx = (int)(rect.left * fspx);
		int nNewy = (int)(rect.top * fspy);
		int nNewWidth = (int)(rect.Width() * fspx);
		int nNewHeight = (int)(rect.Height() * fspy);

		// 信息赋给结构体
		ControlPos windowedCtrl, maxedCtrl;
		windowedCtrl.Left = rect.left;
		windowedCtrl.Top = rect.top;
		windowedCtrl.CtrlWidth = rect.Width();
		windowedCtrl.CtrlHeight = rect.Height();
		maxedCtrl.Left = nNewx;
		maxedCtrl.Top = nNewy;
		maxedCtrl.CtrlWidth = nNewWidth;
		maxedCtrl.CtrlHeight = nNewHeight;
		// 位置信息加入新旧容器
		m_vecWindowedControl.push_back(windowedCtrl);
		m_vecMaxedControl.push_back(maxedCtrl);
		//调整控件
		pWnd->MoveWindow(nNewx, nNewy, nNewWidth, nNewHeight);

		//获取下一个子窗口(控件)
		pWnd = pWnd->GetWindow(GW_HWNDNEXT);
	}
}

然后是剩下两个下属页面的缩放,与1中的实现相同,这个最主要的问题在于先缩放主页面,然后在主页面缩放的过程中会对下属页面进行缩放。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章