功能:
1、绘制的图形包括直线、椭圆和矩形,通过菜单对绘制的图形切换
2、在视图中使用鼠标动态的绘制图形
分析:
当窗口尺寸发生变化时,引起窗口重绘,会发送WM_PAINT消息,这时首先会擦除窗口的背景,然后再进行重绘操作,这样就把窗口中先前绘制的图形擦除掉了;可以将绘制图形的三要素(起点、终点、绘制类型)保存下来,在窗口重绘调用程序视图类窗口的OnDraw函数中再将图形根据保存的三要素重新输出;
1、数据
在视图中添加两个点坐标
CPoint m_ptOrigin; //起点坐标
CPoint m_ptEnd; //终点坐标
UINT m_nDrawType; //图形的类型,0:不绘制;1:直线;2:椭圆;3:矩形;
BOOL m_bFlag; //是否绘制图形
2、绘制过程
2.1--LBUTTONDOWN
1)保存图形的起点
2)m_bFlag=TRUE; //开始绘制
2.2--MOUSEMOVE
if(m_bFlag){
//擦除旧线
//画新线
}
2.3--LBUTTONUP
m_bFlag=FALSE; //绘制结束
实现:
创建单文档类型的MFC工程:Graphic,在资源窗口中的菜单栏中添加顶层菜单项“绘图”,并添加下拉菜单项:IDM_DOT(点)、IDM_LINE(直线)、IDM_RECTANGLE(矩形)、IDM_ELLIPSE(椭圆);在工程的视图类CGraphicView类中添加私有成员变量:UINT m_nDrawType,并在视图类的构造函数中将其初始化为0,在不同的菜单项的消息响应函数中将m_nDrawType设为不同的值以标识不同的绘制图形:点--1、直线--2、矩形--3、椭圆--4;
在视图类CGraphicView类中添加私有成员变量:CPoint m_ptOrigin,并在视图类的构造函数中将该变量初始化为0,在鼠标左键按下的消息响应函数中用以保存左键按下时的坐标;
void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_ptOrigin=point;
CView::OnLButtonDown(nFlags, point);
}
在鼠标左键松开消息响应函数中实现绘图功能:
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
//创建画笔:PS_SOLID/PS_DASH/PS_DOT
CPen pen(PS_SOLID,1,RGB(255,0,0));
CPen* pOldPen=dc.SelectObject(&pen);
//创建画刷,默认的画刷为白色填充,这里创建的是透明无填充的画刷
//CBrush::FromHandle--将句柄转换为对象
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=dc.SelectObject(pBrush);
switch(m_nDrawType){
case 1:
//画点
dc.SetPixel(point,RGB(255,0,0));
break;
case 2:
//画线
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
case 3:
//画矩形线框,无填充
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case 4:
//画椭圆线框,无填充
dc.Ellipse(CRect(m_ptOrigin,point));
break;
}
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
CView::OnLButtonUp(nFlags, point);
}
右击工程名称增加一个新类:CGraph:
为CGraph类添加3个成员变量:
UINT m_nDrawType 绘制类型
CPoint m_ptOrigin 起点
CPoint m_ptEnd 终点
1、添加“设置”对话框
在工程的菜单栏资源的顶层菜单栏“绘图”下添加下拉菜单项“设置”:
为“设置”菜单项添加一个对应的对话框资源,在对话框中允许用户进行相关的绘图设置:
双击对话框资源创建相应的对话框类:
在对话框资源中添加一个静态文本框“线宽”和一个编辑框:
利用MFC类向导为编辑框IDC_LINE_WIDTH关联一个成员变量,用来设置线宽:
为“设置”菜单项添加命令响应,并选择视图类CGraphicView类对此消息进行响应:
因为要在视图类中引用新建的对话框类,故需要在视图类的源文件中包含新建的对话框类的头文件:
#include "SettingDlg.h"
void CGraphicView::OnSetting()
{
// TODO: Add your command handler code here
CSettingDlg dlg;
dlg.DoModal(); //显示对话框
}
在视图类CGraphicView类中添加私有成员变量:UINT m_nLineWidth,并在视图类的构造函数中将其初始化为0,用来保存用户在对话框的线宽编辑框中设置的线宽;
用户在线宽编辑框输入线宽后,若单击【确定】按钮则保存这个线宽值,若单击【取消】按钮则不保存线宽值:
void CGraphicView::OnSetting()
{
// TODO: Add your command handler code here
CSettingDlg dlg;
dlg.m_nLineWidth=m_nLineWidth; //保存上次设置的线宽值
if(IDOK==dlg.DoModal()){
m_nLineWidth=dlg.m_nLineWidth; //调用新填写的线宽值
}
}
在创建画笔对象时,其宽度用m_nLineWidth变量来代替:
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
CPen pen(PS_SOLID,m_nLineWidth,RGB(255,0,0));
CPen* pOldPen=dc.SelectObject(&pen);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=dc.SelectObject(pBrush);
switch(m_nDrawType){
case 1:
dc.SetPixel(point,RGB(255,0,0));
break;
case 2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
case 3:
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case 4:
dc.Ellipse(CRect(m_ptOrigin,point));
break;
}
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
CView::OnLButtonUp(nFlags, point);
}
在“设置”对话框中添加一个设置线型的组框IDC_LINE_STYLE,在组框中放置3个单选按钮,对应“实线”、“虚线”、“点线”,勾选第一个单选按钮的属性中的“组”,使其成为改组单选按钮组的组长;
利用MFC类向导为这组单选按钮关联一个成员变量: int m_nLineStyle,以成员变量的值来区分选中哪个单选按钮:0--实线、1--虚线、2--点线、-1--未选中;
在程序的视图类CGraphicView类中添加私有成员变量:int m_nLineStyle,并在视图类的构造函数中将其初始化为0:
void CGraphicView::OnSetting()
{
// TODO: Add your command handler code here
CSettingDlg dlg;
dlg.m_nLineWidth=m_nLineWidth; //保存上次设置的线宽值
dlg.m_nLineStyle=m_nLineStyle; //保存上次设置的线型
if(IDOK==dlg.DoModal()){
m_nLineWidth=dlg.m_nLineWidth; //调用新填写的线宽值
m_nLineStyle=dlg.m_nLineStyle; //调用新选择的线型
}
}
PS_SOLID/PS_DASH/PS_DOT这些线型在WINGDI.h文件中定义时均对应有0/1/2的值关联,恰好与m_nLineStyle的取值相对应,故可以在创建画笔对象时直接将线型参数替换为m_nLineStyle
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
CPen pen(m_nLineStyle,m_nLineWidth,RGB(255,0,0));
CPen* pOldPen=dc.SelectObject(&pen);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=dc.SelectObject(pBrush);
switch(m_nDrawType){
case 1:
dc.SetPixel(point,RGB(255,0,0));
break;
case 2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
case 3:
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case 4:
dc.Ellipse(CRect(m_ptOrigin,point));
break;
}
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
CView::OnLButtonUp(nFlags, point);
}
2、添加颜色对话框
利用MFC提供的CColorDialog类可创建一个颜色对话框:
CColorDialog(
COLORREF clrInit = 0, //指定默认的颜色选择,默认为黑色
DWORD dwFlags = 0, //指定一组标记,用来定制颜色对话框的功能和外观
CWnd* pParentWnd = NULL //指向颜色对话框父窗口或拥有者窗口的指针
);
在程序的菜单栏资源中的顶层菜单项“绘图”中增加“颜色”下拉菜单项:
选择视图类CGraphicView类对此菜单项的命令做出相应:
为视图类CGraphicView类添加一个私有成员变量:COLORREF m_clr,并在视图类的构造函数中将其初始化为黑色,用于保存用户选择的颜色;
m_clr=RGB(0,0,0);
void CGraphicView::OnColor()
{
// TODO: Add your command handler code here
CColorDialog dlg;
/*CColorDialog类有一个CHOOSECOLOR结构体类型的成员变量m_cc,
m_cc.rgbResult中就保存了用户所选择的颜色*/
dlg.m_cc.Flags |= CC_RGBINIT; //设置颜色对话框选中已选颜色
dlg.m_cc.rgbResult=m_clr; //保存上次设置的颜色
if(IDOK==dlg.DoModal()){
m_clr=dlg.m_cc.rgbResult; //调用新选择的颜色
}
}
在创建画笔时,将颜色参数替换为变量m_clr
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
CPen pen(m_nLineStyle,m_nLineWidth,m_clr);
CPen* pOldPen=dc.SelectObject(&pen);
CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
CBrush *pOldBrush=dc.SelectObject(pBrush);
switch(m_nDrawType){
case 1:
dc.SetPixel(point,m_clr);
break;
case 2:
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
break;
case 3:
dc.Rectangle(CRect(m_ptOrigin,point));
break;
case 4:
dc.Ellipse(CRect(m_ptOrigin,point));
break;
}
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
CView::OnLButtonUp(nFlags, point);
}
3、添加字体对话框
利用MFC提供的CFontDialog类创建字体对话框:
CFontDialog(
LPLOGFONT lplfInitial = NULL, //指向LOGFONT结构体的指针,允许用户设置一些字体的特征
DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, //设置一个或多个与选择的字体相关的标记
CDC* pdcPrinter = NULL, //指向打印设备上下文的指针
CWnd* pParentWnd = NULL //指向字体对话框父窗口的指针
);
在程序菜单栏资源的顶层菜单项“绘图”中添加“字体”下拉选项:
为“字体”菜单项添加命令响应,选择视图类CGraphicView类对此命令做出响应,在响应函数中创建并显示字体对话框:
在工程的视图类CGraphicView类中添加一个私有成员变量:CFont m_font,用以保存用户选择的字体;再在视图类中添加另一个私有成员变量:CString m_strFontName,并在视图类的构造函数中将其初始化为空,用以保存所选字体的名称;
m_strFontName="";
void CGraphicView::OnFont()
{
// TODO: Add your command handler code here
CFontDialog dlg;
//CFontDialog类有一个CHOOSEFONT结构体类型的数据成员:m_cf
//m_cf.lpLogFont--指向逻辑字体的指针
//m_cf.lpLogFont->lfFaceName--存放字体的名称
if(IDOK==dlg.DoModal()){
//设置新字体前先判断是否已有指定的字体,如果有就先释放该字体资源
if(m_font.m_hObject){
m_font.DeleteObject();
}
m_font.CreateFontIndirect(dlg.m_cf.lpLogFont); //调用指定的逻辑字体
m_strFontName=dlg.m_cf.lpLogFont->lfFaceName; //保存所选字体的名称
Invalidate(TRUE); //窗口重绘,以在视图类的OnDraw函数中显示所选字体
}
}
void CGraphicView::OnDraw(CDC* pDC)
{
CGraphicDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
CFont *pOldFont=pDC->SelectObject(&m_font);
pDC->TextOut(0,0,m_strFontName);
pDC->SelectObject(pOldFont);
}
4、示例对话框
在程序的“设置”对话框中添加一个组框,当用户选择线宽和线型后能直接在组框中显示示例;
当线宽的编辑框中的文本改变时,会向其父窗口(对话框)发送EN_CHANGE消息;当单选按钮被点击时,也会向其父窗口(对话框)发送BN_CLICKED消息;对话框的CSettingDlg类,需要捕获这两个通知消息,以反映出用户所做的改变;利用MFC类向导为CSettingDlg类添加编辑框(IDC_LINE_WIDTH)的EN_CHANGE消息的响应函数,和三个单选按钮的BN_CLICKED消息响应函数:
不可能在编辑框及单选按钮的这4个消息响应函数中都绘制示例线条,可以在这4个响应函数中调用Invalidate函数进行窗口重绘,均会发送WM_PAINT消息,只需在该WM_PAINT消息的响应函数中绘制示例线条即可;为对话框类CSettingDlg类添加WM_PAINT消息的响应函数;
void CSettingDlg::OnChangeLineWidth()
{
// TODO: If this is a RICHEDIT control, the control will not
// send this notification unless you override the CDialog::OnInitDialog()
// function and call CRichEditCtrl().SetEventMask()
// with the ENM_CHANGE flag ORed into the mask.
// TODO: Add your control notification handler code here
Invalidate(TRUE);
}
void CSettingDlg::OnRadio1()
{
// TODO: Add your control notification handler code here
Invalidate(TRUE);
}
void CSettingDlg::OnRadio2()
{
// TODO: Add your control notification handler code here
Invalidate(TRUE);
}
void CSettingDlg::OnRadio3()
{
// TODO: Add your control notification handler code here
Invalidate(TRUE);
}
void CSettingDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
UpdateData(); //当对话框中的控件值发生改变时要先更新一下
CPen pen(m_nLineStyle,m_nLineWidth,RGB(0,0,0));
dc.SelectObject(&pen);
CRect rect;
GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect); //获取“示例”组框的矩形区域大小
ScreenToClient(&rect); //由屏幕坐标转化为客户坐标
dc.MoveTo(rect.left+20,rect.top+rect.Height()/2);
dc.LineTo(rect.right-20,rect.top+rect.Height()/2);
// Do not call CDialog::OnPaint() for painting messages
}