转自:http://hi.baidu.com/orchard/item/860eb5c067bce82847d5c0f5
首先本文的代码来自《visual c++面向对象编程教程第2版》,本人拿来练手,并稍加总结,整理,评述,以飨读者。
涉及到的知识点:
1.鼠标信息的处理
2.深刻理解文档和视图的关系
3.新建一个类及其使用方法的技巧。
4.改变窗口大小或者刷新窗口后原来所绘制的图形没有显示出来的问题。
程序软件:VC++6.0
绘制图形的原理:鼠标被用作画笔,绘图过程中要进行不同的鼠标消息的处理,如按下鼠标,移动鼠标和释放鼠标。当用户按下鼠标左键是必须记录鼠标当前的位置,并捕获鼠标,设置光标形状;当移动鼠标时,先判断鼠标左键是否同时被按住,如果是则从上一个鼠标位置到当前鼠标位置绘制一条直线。并保存当前鼠标位置点,供绘制下一段直线用,当释放鼠标左键时将鼠标释放给系统。
程序实现:
1.使用MFC AppWizard 应用程序向导创建一个SDI应用程序MyDraw,在视图类CMyDrawView.h中添加如下代码:
protected: // create from serialization only
CMyDrawView();
DECLARE_DYNCREATE(CMyDrawView)
CPoint m_ptOrigin; //起始点坐标
bool m_bDragging; //鼠标是否处于拖拽状态标记
HCURSOR m_hCross;//拖拽状态时鼠标的样式
2.在构造函数中对拖拽标记和鼠标样式进行初始化
CMyDrawView::CMyDrawView()
{
// TODO: add construction code here
m_bDragging=false;//初始化为false
m_hCross=AfxGetApp()->LoadStandardCursor(IDC_CROSS);//十字光标
}
3 。使用类向导 为视图类添加按下鼠标左键,移动鼠标,释放鼠标左键等消息处理函数。代码分别为:
void CMyDrawView::OnLButtonDown(UINT nFlags, CPoint point) //按下鼠标左键
{
// TODO: Add your message handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
SetCapture();
::SetCursor(m_hCross);
m_ptOrigin=point;
m_bDragging=true;
}
void CMyDrawView::OnLButtonUp(UINT nFlags, CPoint point) //释放鼠标左键
{
// TODO: Add your message handler code here and/or call default
// CView::OnLButtonUp(nFlags, point);
if(m_bDragging)
{
m_bDragging=false;
ReleaseCapture();// 教程上此句有误,Release Capture()中间不能有空格
}
}
void CMyDrawView::OnMouseMove(UINT nFlags, CPoint point) //移动鼠标
{
// TODO: Add your message handler code here and/or call default
CView::OnMouseMove(nFlags, point);
if(m_bDragging)
{
CMyDrawDoc * pDoc=GetDocument();
ASSERT_VALID(pDoc);
// pDoc->AddLine(m_ptOrigin,point);
CClientDC dc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin=point;
}
}
4.在MyDraw.CPP中设置窗口标题
m_pMainWnd->SetWindowText("雪豹出击-----简单的绘图程序");
此后,编译、链接,运行程序后可以将鼠标当作一个画笔绘制曲线了。如图所示:
但是,当你打开了其他任何窗口,在显示此窗口,或者将此窗口进行任何大小变化时,“雪豹出击”四个字就没有了。其原因:
此时调用的是视图类的刷新函数OnDraw(),而在该函数中并没有实现图形绘制功能,为了解决此缺陷必须在OnDraw()函数中重新绘制以前用鼠标所绘制的线段,在利用鼠标绘制时必须将线段的坐标数据保存起来。
二 功能的修补
为线段定义一个CLine的类,线段的起点和终点坐标作为类的成员变量,并定义相应的成员函数。在文档类中为线段选择一个合适的动态数据结构,用于保存大量的且数目不确定的直线图形对象。
在CLine.h中定义:
private:
CPoint m_pt1;//两个私有数据成员 分别表示一条线段的起点和终点。
CPoint m_pt2;
public:
CLine(CPoint pt1,CPoint pt2);//自定义构造函数
void DrawLine(CDC *pDC);//声明绘制线段的成员函数。
在CLine.cpp中:
CLine::CLine(CPoint pt1,CPoint pt2)//使用自定义构造函数初始化起点和终点
{
m_pt1=pt1;
m_pt2=pt2;
}
void CLine::DrawLine ( CDC *pDC)//绘制线段
{
pDC->MoveTo(m_pt1);
pDC->LineTo(m_pt2);
}
2 文档类主要是用来保存数据的,那么在文档类CMyDrawDoc文档类中定义
#include"Line.h"//包含CLine类头文件
#include <afxtempl.h>//使用模板来需要包含此头文件
protected: // create from serialization only
CMyDrawDoc();
DECLARE_DYNCREATE(CMyDrawDoc)
CPtrArray m_LineArray; //存放线段对象指针的动态数组
// Attributes
public:
CLine * GetLine( int nIndex); //获取指定序号线段对象的指针
void AddLine(CPoint pt1,CPoint pt2);//向动态数组中添加新的线段对象的指针
int GetNumLines();//获取线段的向量
成员函数的实现:
void CMyDrawDoc::AddLine (CPoint pt1,CPoint pt2)
{
CLine *pLine=new CLine(pt1,pt2);
m_LineArray.Add(pLine);
}
CLine * CMyDrawDoc::GetLine(int nIndex)
{
if( nIndex<0||nIndex>m_LineArray.GetUpperBound())
return NULL;
return (CLine *)m_LineArray.GetAt (nIndex);
}
int CMyDrawDoc::GetNumLines()
{
return m_LineArray.GetSize();
}
3.当移动鼠标时,除了绘制,还要保存当前线段的起点和终点坐标,代码如下:
void CMyDrawView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CView::OnMouseMove(nFlags, point);
if(m_bDragging)
{
CMyDrawDoc * pDoc=GetDocument();
ASSERT_VALID(pDoc);
pDoc->AddLine(m_ptOrigin,point);//加入线段到指针数组
CClientDC dc(this);
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
m_ptOrigin=point;
}
}
4.在改变程序窗口大小或重新打开窗口时显示这些窗口中的原有图形,必须在OnDraw()函数中重新绘制前面利用鼠标所绘制的线段,这些线段的坐标为CLine类的成员变量,所有的CLine对象的指针已保存在动态数组m_LineArray中。
void CMyDrawView::OnDraw(CDC* pDC)
{
CMyDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
int nIndex=pDoc->GetNumLines();
while(nIndex--)
{
pDoc->GetLine(nIndex)->DrawLine(pDC);
}
}
至此程序完整了。
在第一阶段的时候,由于视图的OnDraw()函数里面没有实现绘图的函数,在改变窗口大小后,程序刷新重画窗口,也就什么东东都没有了。修补程序后,OnDraw()函数取得线段的数量nIndex,然后用一个while循环,GetLine(nIndex)取得指定序号线段对象的指针,它是一个CLine类型的对象。然后调用CLine类的DrawLine()绘制已保存的所有线段。