现在的位置: 首页 > 综合 > 正文

孙鑫VC学习笔记:第十一讲 (二) 图形的保存与重绘方法一

2013年08月13日 ⁄ 综合 ⁄ 共 2697字 ⁄ 字号 评论关闭
图形的保存和重绘

编写画图代码,设定一个标识,在OnLButtonDown中保存鼠标按下去的点,在OnLButtonUp中捕获鼠标弹起的点,利用switch语句分别画图。这是上节课的内容,上节课还讲了窗口重绘的原理,实际上分为两步,
首先擦除以前的背景,然后再进行窗口重绘。
所以当拖动窗口改变窗口大小时,窗口要发生重绘,首先会擦除以前的背景,于是先前所画图像会消失。

解决办法是将画图代码写在OnDraw()函数中,窗口每次重绘都会调用该函数重新绘制图像。

但是怎么保存每次重绘图像需要的代码呢?
事实上,要画的图形由三个因素决定:
 1.图形类型(点、直线、矩形、椭圆)
 2.图形绘制初始点
 3.图形绘制终点
 所以我们可以用数组类保存三个变量:m_nDrawType,m_ptOrigin,m_ptEnd。

首先需要新建一个Class type为Generic Class的名为CGraph类,
在CGraph中增加三个成员变量UINT m_nDrawType,CPoint m_pOrigin,CPoint m_pEnd,
并在不带参数构造函数中将三个成员变量初始化。
然后构造一个带参数的构造函数 CGraph(UINT m_nDrawType,CPoint m_pOrigin,CPoint m_pEnd);
用来接收三个变量值,代码如下:

 CGraph::CGraph(UINT m_nDrawType,CPoint m_pOrigin,CPoint m_pEnd)
 {
  this->m_nDrawType=m_nDrawType;
  this->m_pOrigin=m_pOrigin;
  this->m_pEnd=m_pEnd;
 }
 
至此,我们可以用CGraph类保存绘制一个图形的三要素,根据这三要素可以绘制我们所作的图形。
--------------------------------------------------------------------------------
但是如果我们绘制了多个图形怎么办?
如果用CGraph类对象的一个数组,那么它只能保存有限个对象,
而我们在绘制图形的时候,并不能限定只能绘制多少个图形。
用链表可以实现动态存储,但是操作十分复杂,
于是我们想到了MFC提供一集合类,如前面用到过的CStringArray,可以动态存储CString对象
这里要用到的是另一个集合类,CPtrArray,它用来动态保存CGraph对象。
(它的用法大家可以参见 MSDN或右边。)

于是在OnLButtonUp函数中画完图形之后 ,我们想创建一个CGraph对象,
把画这个图形所必须的三要素保存到这个对象之中。
如果我们直接在尾部增加下面两行代码:
  graph(m_nDrawType,m_ptOrigin,point)
 m_ptrArray.Add(&graph);
但是运行程序之后,我们所绘制的图形没能在OnDraw函数中重绘。
这是因为我们在OnLButtonUp函数中创建的CGraph对象在本函数结束时被销毁,内存被回收。
---------------------------------------------------------------------------------
解决办法:
 在OnLButtonUp函数中创建的CGraph对象指针,然后用new方法为它在堆中分配内存。
 因为new方法分配的内存在堆中,除非显式地调用Delete方法去释放它,
 否则其生命周期直到程序结束时才结束 。
 CGraph *=new CGraph(m_nDrawType,m_ptOrigin,point);
 m_ptrArray.Add(pGraph);
 
 虽然pGraph 指针在函数结束时也会析构,但是没有关系,m_ptrArray已经保存了该指针,
 而new所建立的对象是在堆上的,除非显式地调用Delete方法去释放它,
 否则其生命周期直到程序结束时才结束 。

窗口重绘会调用OnDraw函数,所以要在此函数中增加画图代码,代码如下:

注:
 1. m_ptrArray.GetSize()从得到数组长度,构造for循环
 2. ((CGraph*)m_ptrArray.GetAt(i))->得到CGraph类的各变量,
 3. 其中m_ptrArray.GetAt(i)返回CObject*,要强制转换成CGraph*。
--------------------------------------------------------------------------------

下面研究一下,OnDraw函数为什么能在窗口重绘过程中被调用
OnDraw函数不是WM_PAINT的相应函数,为什么能在窗口重绘过程中被调用?
下面是一段MFC资源文件中的代码:(是WM_PAINT消息响应函数OnPaint())
void CView::OnPaint()
 {
 // standard paint routine
  CPaintDC dc(this);
  OnPrepareDC(&dc);
  OnDraw(&dc);
 }
从上面的代码我们知道,原来在基类View类中调用了OnDraw函数,所以我们认为OnDraw专门用来重绘的
其实我们也可以增加WM_PAINT消息响应,自己在其响应函数写重绘窗口的代码。
前面讲过,在响应WM_PAINT时候,只能利用BeginPaint()获得dc的句柄,用EndPaint()释放dc句柄。
而上面代码并没有发现BeginPaint()与EndPaint(),查看一下CPaintDC 就知道,
CPaintDC 类的构造函数中调用了BeginPaint(),析构函数中调用了EndPaint()。

注意:CPaintDC只能在OnPaint中使用,因为在MSDN中这样说了:
CClientDC objects encapsulate working with a device context that represents only the client area of a window. The CClientDC constructor calls the GetDC function, and the destructor calls the ReleaseDC function. CWindowDC objects encapsulate a device context that represents the whole window, including its frame.
如果要在OnPaint以外地方获得dc句柄的话,调用GetDC ,释放用ReleaseDC。

 

抱歉!评论已关闭.