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中的实现相同,这个最主要的问题在于先缩放主页面,然后在主页面缩放的过程中会对下属页面进行缩放。