图形的保存和重绘
编写画图代码,设定一个标识,在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。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/teshorse/archive/2006/08/14/1061437.aspx