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