在编程时碰到了窗口大小改变时控件位置的调整问题。这里在单文档和对话框中分别介绍一下这种方法的实现。第一部分针对 MFC 单文档程序中改变窗口的大小时,视图中的控件位置能够以相应的比例进行调整。第二部分针对对话框应用程序。
一、MFC SDI中控件和背景自适应窗口最大化和拉伸缩放的实现
1.控件
示意图1和示意图2展示了窗口自由缩放,界面中的控件相对位置不变的效果。
示意图1
示意图2
示意图中的数字1和2标识的是该段线段的长度,同一幅图中数值相同表示距离相等。
实现效果即:无论界面如何变化控件的相对位置保持不变。
前面的博文中介绍了如何在单文档程序中添加Button的方法,这里就不再重复。创建Button对象之后,当界面大小改变时,位置需要调整,这就需要捕获当前界面大小的信息,需要用到一个函数Onsize,先讲一下在代码中的实现,再扩展。
代码实现:
void CTerisView::OnSize(UINT nType, int cx, int cy)
{
//cx
//指定工作区的新的宽度
//cy
//指定工作区的新的高度
m_btnStart.MoveWindow(40,10,60,30);
m_btnEnd.MoveWindow(40,10,60,30);
//计算控件的位置
m_btnPause.MoveWindow((cx-20)/3,10,60,30);
m_btnResume.MoveWindow((cx-20)/3,10,60,30);
m_btnSetting.MoveWindow((cx*2-160)/3,10,60,30);
m_btnAbout.MoveWindow(cx-100,10,60,30);
CView::OnSize(nType, cx, cy);
}
这个函数会在窗口大小改变结束后被调用,通常会在这个函数里重新摆放各个控件的位置及大小。红色加粗部分是通过数学计算得到的表达式,数值2标识的长度相等,通过数学关系或得控件起始坐标的表达式。控件大小在此处是通过后两个参数设置的。
这里再扩展一下,MFC窗口变化消息还有两个OnSizing()和OnGetMinMaxInfo()。
三个消息分别是:WM_SIZE、WM_SIZING、WM_GETMINMAXINFO;分别对应相应的处理函数:OnSize、OnSizing、OnGetMinMaxInfo。
void CXXXView::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
lpMMI->ptMinTrackSize.x = 500; //x宽度
lpMMI->ptMinTrackSize.y = 100; //y高度
CView::OnGetMinMaxInfo(lpMMI);
}
说明:对于单文档应用程序同样适用,这里的Dialog需要对应View,下同。
OnGetMinMaxInfo
这个函数在窗口初始化的时候会被调用一次,当窗口大小发生改变的时候也会被调用。利用这个函数,可以比较方便的实现窗口最大最小尺寸的控制。
参数lpMMI是一个结构体指针,其中包含了有关窗口的最大化大小和位置以及最小、最大跟踪大小的信息。
使用这个函数控制窗口最小尺寸的示例的代码如下:
void CXXXView::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
lpMMI->ptMinTrackSize.x = 500; //x宽度
lpMMI->ptMinTrackSize.y = 100; //y高度
CView::OnGetMinMaxInfo(lpMMI);
}
以上代码可以使得窗口大小变化时,最小宽度为500px,最小高度为100px。
OnSizing
这个函数在窗口大小发生变化时被调用。在这个函数里,也可以控制窗口的最大最小尺寸,但是没有OnGetMinMaxInfo方便。
void CXXXView::OnSizing(UINT fwSide, LPRECT pRect)
{
if ((pRect->right - pRect->left) < 500)
{
//return ; //直接return是无效的,窗口大小还是会改变
pRect->right = pRect->left + 500;
}
CView::OnSizing(fwSide, pRect);
}
用上面的方法,如果是从右边改变大小,可以达到想要的效果,但是从左边改变大小,虽然大小可以控制在最小500,但是当达到最小宽度后,再缩小,会发现整个窗口往右移动了,原因是代码中的pRect->right = pRect->left + 500;这句是针对left来改变right的,所以left移动了,right也移动了,看上去就像是这个窗口右移了。所以针对这种情况又要另外做相应的处理。
2.背景
这里的背景指的是View类中绘制的背景区域。如示意图中的绿色的区域。
实现方法,在View类的OnDraw函数中添加如下的代码:
//获取主窗口的大小
CRect rect;
GetWindowRect(&rect);
//游戏区域绘制 背景及其他
CPen pen_gamearea;
CPen*myoldpen_gamearea;
//创建蓝色的画笔
pen_gamearea.CreatePen(PS_SOLID,3,RGB(0,0,255));
myoldpen_gamearea=pDC->SelectObject(&pen_gamearea);
pDC->MoveTo(0,rect.Height()-20);//到左下顶点 左侧边线和左边框重合 不绘制边线
pDC->LineTo(rect.Width()*2/3,rect.Height()-20); //底部边线
pDC->LineTo(rect.Width()*2/3,51); //右部边线
//方块游戏区域
CRect rect_gamearea(0,50,rect.Width()*2/3,rect.Height()-20);
CBrush mybrush_gamearea;
//创建画刷 绿色
mybrush_gamearea.CreateSolidBrush(RGB(0,222,0));
//绘制背景
pDC->FillRect(rect_gamearea,&mybrush_gamearea);
pDC->SelectObject(myoldpen_gamearea);
说明:关键还是红色加粗部分,即获取客户区的size,这个值随窗口大小的改变而改变。
注:由于OnDraw和创建控件的时机不同,以及MFC消息响应机制的关系,这个值不能够应用于控件位置的动态调整。
上面的处理是在单文档中进行,下面在扩展一下在MFC基于对话框应用程序实现类似的效果。
二、MFC对话框控件和背景图片自适应窗口最大化和拉伸缩放的实现
假设创建的对话框的类名是CMyDlg,具体过程如下:
步骤一:把easysize.h拷贝到CMyDlg项目文件夹中,同时在CMyDlg的.h文件和.cpp文件中加入#include"easysize.h";
or(#include EasySize.h to your stdafx.h (or put it in your include directory and #include <EasySize.h> , which I recommend)
步骤二:在CMyDlg类的h文件中,加入DECLARE_EASYSIZE,注意结尾处不要加“;”。
class CMyDlg : public CDialog
{
DECLARE_EASYSIZE
// Construction
…
}
步骤三:在CMyDlg类的OnInitDialog()函数的结尾处加入INIT_EASYSIZE, 注意此处结尾处要加“;”。
BOOL CMyDlg::OnInitDialog()
{ …
// TODO: Add extra initialization here
INIT_EASYSIZE;
return TRUE; // return TRUE unless you set the focus to a control
}
步骤四:增加WM_SIZE消息响应函数OnSize(),在函数中加入UPDATE_EASYSIZE,注意此处结尾处要加“;”。
void CMyDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
UPDATE_EASYSIZE;
}
步骤五:在CMyDlg的cpp文件中添加EASYSIZE 的宏映射
BEGIN_EASYSIZE_MAP(CMyDlg)
EASYSIZE(control,left,top,right,bottom,options)
END_EASYSIZE_MAP
注意:如果没有添加EASYSIZE 的宏映射就开始编译的话,会出现链接错误!
这里需要解释的是宏EASYSIZE()的用法,其原型如下:
EASYSIZE(control,left,top,right,bottom,options)
该宏表示对ID值为control的控件实施缩放效果,缩放后的上下左右位置由control,left,top,right,bottom来确定,大小由option确定。
其中:control为对话框中的控件ID值,left,top,right,bottom四个参数为控件位置的坐标,其值可以选择ES_BOARD,ES_KEEPSIZE, 控件ID值三者之一。Options可以为ES_HCENTER, ES_VCENTER的结合,options可置0。
ES_BOARD表示控件与对话框边界(以下简称边界)的距离;
ES_KEEPSIZE表示控件水平/垂直方向上尺寸保持不变;
控件ID值表示当前控件与指定控件之间的距离;
ES_HCENTER表示缩放后控件在指定位置内水平居中;
ES_VCENTER表示缩放后控件在指定位置内垂直居中;
例如:
EASYSIZE(IDOK,ES_BORDER,ES_BORDER,ES_BORDER,ES_BORDER,0)
表示缩放后,值为IDOK的控件,距离边界上下左右位置保持不变,水平和垂直方向尺寸拉伸;
EASYSIZE(IDOK,ES_BORDER,ES_BORDER,ES_BORDER,ES_BORDER,ES_HCENTER)
表示缩放后,值为IDOK的控件,距离边界上下位置保持不变,垂直方向尺寸拉伸,水平居中;
EASYSIZE(IDOK,ES_BORDER,ES_BORDER,ES_BORDER,ES_BORDER,ES_HCENTER| ES_HCENTER)
表示缩放后,值为IDOK的控件,在对话框内垂直居中,水平居中;
EASYSIZE(IDOK,ES_BORDER,ES_KEEPSIZE,ES_KEEPSIZE,ES_BORDER,0)
表示缩放后,值为IDOK的控件,距离边界左、下方位置保持不变,同时保持控件尺寸;
EASYSIZE(IDOK,ES_BORDER,ES_KEEPSIZE, ES_BORDER,ES_BORDER,0)
表示缩放后,值为IDOK的控件,距离边界左、右、下方位置保持不变,水平方向尺寸拉伸,垂直方向尺寸不变;
EASYSIZE(IDOK,ES_BORDER,ES_BORDER,IDCANCEL,ES_BORDER,0)
表示缩放后,值为IDOK的控件,距离边界上下左位置保持不变,距离ID值为IDCANCEL的右方位置距离保持不变,水平和垂直方向尺寸拉伸;(当使用指定控件作为定位参数时候,确保指定控件的EASYSIZE在该宏前面)
(注)第二部分内容为转载