MFC学习笔记之图形保存与重绘

  • Post author:
  • Post category:其他


先增加一个新的菜单项 绘图 ,然后在里面增加4个子菜单项 点 线 矩形 椭圆 ,在View类中响应各个子菜单项,为View类增加一个私有数据成员 int m_nDrawType 用来保存用户所做的选择 这个和上一篇日志的一样 所以代码不贴了,然后就是响应 OnLButtonDown 和 ONLButtonUp 消息 进行相应的绘图。我们知道当窗口大小改变或是窗口被切换的时候,程序就会发送一个WM_PAINT消息,窗口就会发生重绘。这个时候我们画的图像就会消失不见,这一次就是要就保存和重绘图像进行一些相关操作。

我们知道窗口重绘时,会调用OnDraw函数,因此我们可以在这个函数中完成图像的输出。

保存图形的方法有很多,在本例中绘制的图像,有三个要素:绘画类型、起点、终点,也就是说对于本例中的图形,只要保存这三个属性就可以了。窗口重绘的时候在OnDraw函数中根据这三个要素就可以进行图像的绘制了。由于这三个参数的类型不同,可以用结构体来保存,在C++中结构体就是一个类,因此,本例可以新建一个新类来保存呢这三个要素。通过单击[Insert/New Class] 就可以进行新类创建了 ,弹出的菜单中 类类型要选择 Generic Class,新类命名为CGraph ,然后为该类增加三个成员变量,都设为public的,后面要用到。分别是:int m_nDrawType; CPoint m_ptOrign; CPoint m_ptEnd;分别保存 绘画类型 起点 终点 三个要素。然后再为该类增加一个有这三个参数的构造函数,让允许用户在构造该类对象时,可以直接通过这三个参数给成员变量赋值:

CGraph::CGraph(int m_nDrawType,CPoint m_ptOrign,CPoint m_ptEnd)
{
	this->m_nDrawType=m_nDrawType;
	this->m_ptOrign=m_ptOrign;
	this->m_ptEnd=m_ptEnd;
}

在绘制图像过程中,我们会画很多个图形,每一个图形都对应一个CGraph的对象,以保存该图像的三个要素。我们可以采用数组来保存这些创建的对象,但是长度未知,只可以存储一定容量的元素,这样不好。用链表的话又比较复杂。本例中,使用MFC的一个集合类:CPtrArray,它支持void 类型的指针数组,该类成员函数与CobArray类相应的函数类似,只是CObArray类的成员函数中使用CObject指针作为参数或者返回值类型的地方,在CPtrArray类中使用void 类型指针替换即可。

在本程序中我们只需要用到它的几个函数,如果要增加一个成员,就用add函数,来增加一个void类型的指针的对象,如果想取得该集合的某个元素就用GetAt方法,如果想获得集合的数目就用GetSize函数。 下面,先为View类增加一个CPtrArray类型的变量m_ptrArray.然后就在每次绘图之后,构造一个CGraph对象,并将该对象的地址保存到m_ptrArray中.

void CMyView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	CClientDC dc(this);
	CPen pen(PS_SOLID,2,RGB(255,0,0));
	dc.SelectObject(&pen);
	//m_dcMetaFile.SelectObject(&pen);
	CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
/*
	if(!m_dcCompatible.m_hDC)
	{
		m_dcCompatible.CreateCompatibleDC(&dc);
		CRect rect;
		GetClientRect(&rect);
		CBitmap bitmap;
		bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
		m_dcCompatible.SelectObject(pBrush);
		m_dcCompatible.SelectObject(&pen);
		m_dcCompatible.SelectObject(&bitmap);
		m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);
	}
*/
//	dc.SelectObject(pBrush);
//	m_dcMetaFile.SelectObject(pBrush);

	CRect rect(m_pOrign,point);

	switch(m_nDrawType)
	{
	case 1:
		dc.SetPixel(point,RGB(0,0,0));
		//m_dcMetaFile.SetPixel(point,RGB(255,0,0));
		//m_dcCompatible.SetPixel(point,RGB(255,0,0));
		break;
	case 2:
		dc.MoveTo(m_pOrign);
		dc.LineTo(point);
		//m_dcMetaFile.MoveTo(m_pOrign);
		//m_dcMetaFile.LineTo(point);
		//m_dcCompatible.MoveTo(m_pOrign);
		//m_dcCompatible.LineTo(point);
		break;
	case 3:
		dc.Ellipse(&rect);
		//m_dcMetaFile.Ellipse(&rect);
		//m_dcCompatible.Ellipse(&rect);
		break;
	case 4:
		dc.Rectangle(&rect);
		//m_dcMetaFile.Rectangle(&rect);
		//m_dcCompatible.Rectangle(&rect);
		break;
	}
	
	//OnPrepareDC(&dc);
	//dc.DPtoLP(&m_pOrign);
	//dc.DPtoLP(&point);
	
	CGraph *graph=new CGraph(m_nDrawType,m_pOrign,point);
	m_ptrArray.Add(graph);

	CScrollView::OnLButtonUp(nFlags, point);
}

其中注释的代码先不理会 后面会说到,在构建CGraph对象的时候要用指针,不可以直接构建,不然会由于是临时变量的关系,导致m_ptrArray中保存的CGraph对象地址中的东西为空。 接下来呢就要在OnDraw函数中将它们重绘的显示出来:

void CMyView::OnDraw(CDC* pDC)
{
	CMyDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here
	
	CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	pDC->SelectObject(pBrush);

	for(int i=0;i<m_ptrArray.GetSize();i++)
	{
		switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType)
		{
		case 1:
			pDC->SetPixel(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd,RGB(0,0,0));
			break;
		case 2:
			pDC->MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrign);
			pDC->LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);
			break;
		case 3:
			pDC->Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrign,((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
			break;
		case 4:
			pDC->Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrign,((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));
			break;
		}
	}
}

因为CPtrArray类的GetAt函数返回的是一个void类型的指针,所以要进行强制的类型转换,讲其转换为CGraph类型的指针,猜可以正确访问CGraph的成员变量(因为在这里要访问,所以才在前面定义CGraph类的时候将这几个变量设为public的)。

virtual void OnDraw( CDC* pDC ) = 0;

这个是CView类的OnDraw函数的定义 是一个纯虚函数。另外在窗口重绘的时候发送的是WM_PAINT消息,如果想让图形始终可以显示出来,就可以讲图形的绘制操作放置在该消息的响应函数OnPaint中,而OnDraw函数并不是WM_PAINT消息的响应函数,但是它为什么可以在窗口发生重绘的时候被调用呢࿱



版权声明:本文为Kay_Sprint原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。