案例目标:在屏幕上出现一个三角形,同时显示各顶点坐标,当用鼠标选择某顶点并拖动时,三角形随鼠标移动而变形。具体步骤为:
一、在VS2022上建立一个基于对话框的MFC应用,项目名称:DrawMovableTriangle,其它步骤使用缺省。进入对话框界面,选择对话框,将其适当拉大一些。然后,删除其自带的控件。
二、打开解决方案资源管理器,找到DrawMovableTriangleDlg.h文件
1、先以public方式声明一个函数:
void drawTriangle(); //这是绘制三角形的主要函数
2、再以protected方式声明三个变量:
CPoint vertexPos[3]; //三角形的三个顶点坐标
BOOL boolLBDown; //鼠标左键按下标志,TRUE按下,FALSE抬起
int vertexNum; //顶点序号
三、利用类向导添加三个消息处理函数
1、鼠标左键按下处理函数
步骤:项目->类向导->确认类名是*Dlg->单击消息->找到并单击WM_LBUTTONDOWN->点击添加处理程序,这样在DrawMovabletriangleDlg.h文件中就有了如下声明:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
2、采用类似步骤,继续添加鼠标移动消息处理函数和左键抬起消息处理函数,如下:
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
以上三个消息处理程序不但在这里进行了声明,同时还在DrawMovableTriangleDlg.cpp中进行了实现,只是实现函数中还没有我们需要的代码,我们需要做的就是将这些代码添上。
四、在DrawMovableTriangleDlg.cpp中找到OnInitDialog()函数,在TODO:行下面,完成几个变量的赋初值。代码如下:
CRect rect;// 定义矩形类对象
GetClientRect(rect);//GetClientRect将窗口客户区宽、高等数据存入rect
vertexPos[0] = CPoint(rect.Width()/2,rect.Height()/4); //上顶点
vertexPos[1] = CPoint(rect.Width()/4,rect.Height()*3/4); //左下顶点
vertexPos[2] = CPoint(rect.Width()*3/4,rect.Height()*3/4); //右下顶点
boolLBDown = FALSE; //默认鼠标左键未按下
vertexNum = 0; //初始绘制三角形从上顶点开始
五、在DrawMovableTriangle.cpp文件中实现动态绘制三角形函数,在这个函数中要使用到双缓冲技术,具体代码如下:
void CDrawMovableTriangleDlg::drawTriangle()
{
CRect rect; //声明矩形结构
GetClientRect(&rect); //获取客户区数据存入rect
CDC* pDC = GetDC();
CDC memDC; //声明一个设备上下文对象(兼容对象)
memDC.CreateCompatibleDC(pDC); //兼容对象与当前设备上下文pDC兼容(实际上兼容对象此时尚无空间)
CBitmap NewBitmap, * pOldBitmap; //声明一个位图对象和一个位图指针
NewBitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); //位图对象与pDC兼容,这个位图可以代替pDC用于作图
pOldBitmap = memDC.SelectObject(&NewBitmap);//位图对象选入兼容对象中,并将原位图信息存入位图指针,此时在memDC上作图实际上是在NewBitmap上作图
memDC.FillSolidRect(rect, pDC->GetBkColor()); //用pDC的背景色填充memDC
//在memDC上作图
memDC.SelectStockObject(GRAY_BRUSH); //将灰色画刷选入兼容对象
for (int i = 0; i < 3; i++) {
CString str;
str.Format((L"x = %d, y = %d"), vertexPos[i].x, vertexPos[i].y); 将三角形顶点坐标组合成字符串
memDC.SetTextColor(RGB(255, 0, 0)); //设置兼容上下文对象的文字颜色
memDC.TextOutW(vertexPos[i].x, vertexPos[i].y, str); //输出字符串到兼容上下文对象的文字颜色
memDC.Rectangle(vertexPos[i].x - 5, vertexPos[i].y - 5, vertexPos[i].x + 5, vertexPos[i].y + 5); //给三角形顶点绘制小方块
if (0 == i) //画出三角形
memDC.MoveTo(vertexPos[i]); //遇三角形第一个顶点则定位
else
memDC.LineTo(vertexPos[i]); //不是顶点时则连线(0-1,1-2)
}
memDC.LineTo(vertexPos[0]); //连接2-0
pDC->BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); //将兼容对象中的图像copy到设备上下文对象中
memDC.SelectObject(pOldBitmap); //恢复兼容对象
NewBitmap.DeleteObject();
/* Windows 显示设备的属性,共有下面几种:位图、画刷、字体、画笔、区域。如果要设置它们到当前(或兼容)设备里,就需要使用SelectObject 函数,比如上面介绍的字体设置,就会用到这个函数。当你创建一个位图时,这时 Windows 就会在内存里分配一块内存空间,用来保存位图的数据。当你创建字体时,也会分配一块内存空间保存字体。如果程序只是分配,而不去删除,就会造成内存使用越来越多,最后导到 Windows 这幢大楼倒下来。如果你忘记删除它,就造成了内存泄漏。因此,当你创建显示设备资源时,一定要记得删除它们啊,否则运行你的程序越长,就导致系统不稳定。记得使用DeleteObject 函数去删除它们,把占用的内存释放回去给系统。*/
memDC.DeleteDC();
/* 如果一个设备上下文环境的句柄是通过调用GetDC函数得到的,那么应用程序不能删除该设备上下文环境,应该调用ReleaseDC函数来释放该设备上下文环境。如果是自己生成的,则用DeleteDC释放所占用的资源 */
}
六、接着,还是在DrawMovableTriangle.cpp中找到那三个消息处理函数中,为其添加相关代码如下:
1、在void CDrawMovableTriangleDlg::OnLButtonDown(UINT nFlags, CPoint point)中TODO:行下面,添加一句代码:
boolLBDown = TRUE;
2、在176 void CDrawMovableTriangleDlg::OnMouseMove(UINT nFlags, CPoint point)中TODO:行下面添加:
CDC* pDC = GetDC();
for (int i = 0; i < 3; i++) {
if (point.x<vertexPos[i].x + 5 && point.x>vertexPos[i].x - 5 && point.y<vertexPos[i].y + 5 && point.y>
vertexPos[i].y - 5) { //判断鼠标是否在三角形顶点上下或左右5个像素范围内
SetCursor(LoadCursor(NULL, IDC_HAND));
/*上面的设置光标样式为手型,LoadCursor加载windows 自带的光标资源时第一个参数必须为空*/
vertexNum = i;
} //标记被点击的三角形顶点序号
}
if (boolLBDown) vertexPos[vertexNum] = point;//将鼠标位置付给顶点,这里鼠标状态是即按下又在移动
ReleaseDC(pDC);//释放pDC,从用此函数也证明pDC不是memDC,若是memDC须用memDC.DeleteDC();
drawTriangle(); //调用动态画三角形程序
3、在207 void CDrawMovableTriangleDlg::OnLButtonUp(UINT nFlags, CPoint point)中TODO:行下面添加:
boolLBDown = FALSE; //恢复左键弹起设置,否则选上三角形定点后放不开
五、运行效果截图: