功能:
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 }