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中的實現相同,這個最主要的問題在於先縮放主頁面,然後在主頁面縮放的過程中會對下屬頁面進行縮放。

 

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