MFC(绘图与保存,孙鑫C++第十一讲笔记整理)

前面画图步骤直接上略了,因为跟第十讲的步骤是一样的,这里不再累赘

1.创建4个菜单,为其添加消息响应,用成员变量保存绘画类型。添加LButtonDownUp消息。

 

2.当窗口重绘时,如果想再显示原先画的数据,则需要保存数据。为此创建一个新类来记录绘画类型和两个点。

class CGraph 

{

public:

 CPoint m_ptOrigin;//起点

 CPoint m_ptEnd;//终点

 UINT m_nDrawType;//绘画类型

 CGraph();

 CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);//此为构造函数。

 virtual ~CGraph();};

  然后在void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)中加入如下代码

 //CGraph graph(m_nDrawType,m_ptOrigin,point);//不能用局部变量

 //m_ptrArray.Add(&graph);//加入这种指针数组中

/* OnPrepareDC(&dc);//这个函数中可以重新设置窗口原点,对于滚动条中,保存数据前要调用此函数

 dc.DPtoLP(&m_ptOrigin);//将设备坐标转换为逻辑坐标

 dc.DPtoLP(&point);//

 CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);//在堆中创建新的对象

 m_ptrArray.Add(pGraph);*///加入到指针数组中

GraphicView.h中有如下代码

 CPtrArray m_ptrArray;

  OnDraw中重画时调出数据

 for(int i=0;i<m_ptrArray.GetSize();i++)

 

3.CView::OnPaint()调用了OnDraw(),但在void CGraphicView::OnPaint()MFCWizard没有调用OnDraw(),要注意这个区别。如果你此时想调用,必须手动添加代码。 OnDraw(&dc);

 

4.让窗口具有滚动条的功能。

  1.CGraphicView的头文件中的CView全部替换成CSrollView

  2.添加如下的代码

void CGraphicView::OnInitialUpdate()

{

 CScrollView::OnInitialUpdate();

 

 // TOD Add your specialized code here and/or call the base class

 SetScrollSizes(MM_TEXT,CSize(800,600));//设置映射模式,设定窗口大小。OK

}

 

5.坐标系的转换,此处不再详细介绍,需要时请查阅相关资料。

 

6.解决重绘时线跑到上面的问题。为什么会错位?因为逻辑坐标和设备坐标没有对应起来。

解决方法:

 OnLButtonDown画完图后,保存之前。调用

/* OnPrepareDC(&dc);//重新设置逻辑坐标的原点!!!

 dc.DPtoLP(&m_ptOrigin);//设备坐标转化为逻辑坐标

 dc.DPtoLP(&point);

 CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);

 m_ptrArray.Add(pGraph);*/

 

7.另外两种方法来保存数据。

 一种是用CMetaFileDC

 另一种是利用兼容DC,重绘时利用 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);

 

void CHuiTuBaoView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	

	/*CClientDC ccdc(this);
	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	ccdc.SelectObject(cbrush);

	switch(m_leixing)
	{
	case 0:
		ccdc.SetPixel(point,RGB(0,0,0));
		break;

	case 1:
		ccdc.MoveTo(m_yuandian);
		ccdc.LineTo(point);
		break;

	case 2:
		ccdc.Rectangle(&CRect(m_yuandian,point));
		break;

	case 3:
		ccdc.Ellipse(&CRect(m_yuandian,point));
		break;
	default:
		break;
	}*/


	//CHUATU huatu(m_leixing,m_yuandian,point);//对象的声明周期在右大括号结束之后就销毁了,所以不可行
	//m_carray.Add(&huatu);

	CHUATU *huatu=new CHUATU(m_leixing,m_yuandian,point);//使用指针,在堆栈分配了空间,与整个程序的声明周期一致,可行
	m_carray.Add(huatu);
	Invalidate();

	CView::OnLButtonUp(nFlags, point);
}

 


 

void CHuiTuBaoView::OnDraw(CDC* pDC)
{
	CHuiTuBaoDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here


	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	pDC->SelectObject(cbrush);

	for(int i=0;i<m_carray.GetSize();++i)
	{
		switch(((CHUATU*)m_carray.GetAt(i))->m_cleixing)
		{
		case 0:
			pDC->SetPixel(((CHUATU*)m_carray.GetAt(i))->m_czhondian,RGB(255,0,0));
			break;
		
		case 1:
			pDC->MoveTo(((CHUATU*)m_carray.GetAt(i))->m_cyuandian);
			pDC->LineTo(((CHUATU*)m_carray.GetAt(i))->m_czhondian);
			break;

		case 2:
			pDC->Rectangle(&CRect(((CHUATU*)m_carray.GetAt(i))->m_cyuandian,((CHUATU*)m_carray.GetAt(i))->m_czhondian));
			
			break;

		case 3:
			pDC->Ellipse(&CRect(((CHUATU*)m_carray.GetAt(i))->m_cyuandian,((CHUATU*)m_carray.GetAt(i))->m_czhondian));
			break;
		default:
			break;
		}
	}

}


上面使用CPtrArray来保存CHUATU的指针,那能不能使用我们熟悉的Vector动态数组来操作呢。试试便知

 

试验证明是可以的。

在CXXView头文件中写上

include<vector>

using namespace std;

然后定义成员变量

vector<CHUATU*>vv;

void CHuiTuBaoView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	

	/*CClientDC ccdc(this);
	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	ccdc.SelectObject(cbrush);

	switch(m_leixing)
	{
	case 0:
		ccdc.SetPixel(point,RGB(0,0,0));
		break;

	case 1:
		ccdc.MoveTo(m_yuandian);
		ccdc.LineTo(point);
		break;

	case 2:
		ccdc.Rectangle(&CRect(m_yuandian,point));
		break;

	case 3:
		ccdc.Ellipse(&CRect(m_yuandian,point));
		break;
	default:
		break;
	}*/


	//CHUATU huatu(m_leixing,m_yuandian,point);
	//m_carray.Add(&huatu);

	/*CHUATU *huatu=new CHUATU(m_leixing,m_yuandian,point);
	m_carray.Add(huatu);
	Invalidate();*/

	CHUATU *huatu=new CHUATU(m_leixing,m_yuandian,point);
	vv.push_back(huatu);
	Invalidate();

	CView::OnLButtonUp(nFlags, point);
}


 

void CHuiTuBaoView::OnDraw(CDC* pDC)
{
	CHuiTuBaoDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here


	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	pDC->SelectObject(cbrush);

	/*for(int i=0;i<m_carray.GetSize();++i)
	{
		switch(((CHUATU*)m_carray.GetAt(i))->m_cleixing)
		{
		case 0:
			pDC->SetPixel(((CHUATU*)m_carray.GetAt(i))->m_czhondian,RGB(255,0,0));
			break;
		
		case 1:
			pDC->MoveTo(((CHUATU*)m_carray.GetAt(i))->m_cyuandian);
			pDC->LineTo(((CHUATU*)m_carray.GetAt(i))->m_czhondian);
			break;

		case 2:
			pDC->Rectangle(&CRect(((CHUATU*)m_carray.GetAt(i))->m_cyuandian,((CHUATU*)m_carray.GetAt(i))->m_czhondian));
			
			break;

		case 3:
			pDC->Ellipse(&CRect(((CHUATU*)m_carray.GetAt(i))->m_cyuandian,((CHUATU*)m_carray.GetAt(i))->m_czhondian));
			break;
		default:
			break;
		}
	}*/

	for(vector<CHUATU*>::iterator itera=vv.begin();itera!=vv.end();++itera)
	{
		switch((*itera)->m_cleixing)
		{
		case 0:
			pDC->SetPixel((*itera)->m_czhondian,RGB(255,0,0));
			break;

		case 1:
			pDC->MoveTo((*itera)->m_cyuandian);
			pDC->LineTo((*itera)->m_czhondian);
			break;

		case 2:
			pDC->Rectangle(&CRect((*itera)->m_cyuandian,(*itera)->m_czhondian));
			break;

		case 3:
			pDC->Ellipse(&CRect((*itera)->m_cyuandian,(*itera)->m_czhondian));
			break;

		}
	}

}


可以使用很多方法解决,不要丢了我们前面学习的C++知识喔。

 

下面是有关GDI映射的问题,具体也可以看我的Windows32GDI映射的博客

 

 

 

 

 

 

  OnPrepareDC会随时根据滚动窗口的位置来调整视口的原点

 

下面是一个关于设置坐标位置的例子。

手动设置滚动条

1class CXXView : public CView--->class CXXView : public CScrollView

2把cpp文件中的CView全部替换成CScrollView

3设置滚动条的属性

void SetScrollSizes( int nMapMode, SIZE sizeTotal, const SIZE& sizePage = sizeDefault, const SIZE& sizeLine = sizeDefault );

 

在CXXView中添加一个虚函数OnInitUpdate函数

4添加一条代码,这个函数是在调用OnDraw之前调用的

void CHuiTuBaoView::OnInitialUpdate() 
{
	CScrollView::OnInitialUpdate();
	
	// TODO: Add your specialized code here and/or call the base class

	SetScrollSizes(MM_TEXT,CSize(800,600));
	
}


然后就可以显示滚动条了:

 

然后用老师讲的方法,可以解决坐标映射问题,上面的PPT有讲问题的现象和解决方法

void CHuiTuBaoView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	

	/*CClientDC ccdc(this);
	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	ccdc.SelectObject(cbrush);

	switch(m_leixing)
	{
	case 0:
		ccdc.SetPixel(point,RGB(0,0,0));
		break;

	case 1:
		ccdc.MoveTo(m_yuandian);
		ccdc.LineTo(point);
		break;

	case 2:
		ccdc.Rectangle(&CRect(m_yuandian,point));
		break;

	case 3:
		ccdc.Ellipse(&CRect(m_yuandian,point));
		break;
	default:
		break;
	}*/


	//CHUATU huatu(m_leixing,m_yuandian,point);
	//m_carray.Add(&huatu);

	/*CHUATU *huatu=new CHUATU(m_leixing,m_yuandian,point);
	m_carray.Add(huatu);
	Invalidate();*/
	
	CClientDC ccdc(this);


	OnPrepareDC(&ccdc);
	ccdc.DPtoLP(&m_yuandian);
	ccdc.DPtoLP(&point);
	CHUATU *huatu=new CHUATU(m_leixing,m_yuandian,point);
	vv.push_back(huatu);
	Invalidate();

	CScrollView::OnLButtonUp(nFlags, point);
}


这样就完美解决了。

OnPaint中调用了OnprepareDC和OnDraw函数,OnprepareDC就是用来调整(设备环境)视口的

 

使用原文件上下文保存图像,CMetaFileDC

在OnLButtonUp中,CMetaFileDC yuanwenjian是成员变量,构造函数中Create

CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	yuanwenjiandc.SelectObject(cbrush);

	switch(m_leixing)
	{
	case 0:
		yuanwenjiandc.SetPixel(point,RGB(0,0,0));
		break;

	case 1:
		yuanwenjiandc.MoveTo(m_yuandian);
		yuanwenjiandc.LineTo(point);
		break;

	case 2:
		yuanwenjiandc.Rectangle(&CRect(m_yuandian,point));
		break;

	case 3:
		yuanwenjiandc.Ellipse(&CRect(m_yuandian,point));
		break;
	default:
		break;
	}


在OnDraw中

void CHuiTuBaoView::OnDraw(CDC* pDC)
{
	CHuiTuBaoDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here


	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	pDC->SelectObject(cbrush);


	HMETAFILE hmetafile;
	hmetafile=yuanwenjiandc.Close();
	pDC->PlayMetaFile(hmetafile);
	yuanwenjiandc.Create();
	DeleteMetaFile(hmetafile);}//一定要关闭,否则出错


如果还想要保存原来的图画,这这样

void CHuiTuBaoView::OnDraw(CDC* pDC)
{
	CHuiTuBaoDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	// TODO: add draw code for native data here


	CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	pDC->SelectObject(cbrush);


	HMETAFILE hmetafile;
	hmetafile=yuanwenjiandc.Close();
	pDC->PlayMetaFile(hmetafile);
	yuanwenjiandc.Create();
	yuanwenjiandc.PlayMetaFile(hmetafile);//保存上一次绘画的图案
	DeleteMetaFile(hmetafile);
}


CMetaFile只是保存的是绘图的命令,而不是文件

下面给打开和保存添加事件

void CHuiTuBaoView::OnFileSave() 
{
 // TODO: Add your command handler code here
 HMETAFILE hmetafile;
 hmetafile=yuanwenjiandc.Close();
 CopyMetaFile(hmetafile,"meta.wmf");
 yuanwenjiandc.Create();
 DeleteMetaFile(hmetafile);
}

void CHuiTuBaoView::OnFileOpen() 
{
	// TODO: Add your command handler code here
	HMETAFILE hmetafile;
	hmetafile=GetMetaFile("meta.wmf");
	yuanwenjiandc.PlayMetaFile(hmetafile);
	DeleteMetaFile(hmetafile);
	Invalidate();

}


使用另外一种方法保存,兼容DC

在OnLButtonUp中

CBrush *cbrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	CClientDC ccdc(this);

	if(!compatibledc)
	{
		compatibledc.CreateCompatibleDC(&ccdc);
		CRect rect;
		GetClientRect(&rect);
		CBitmap bitmap;
		bitmap.CreateCompatibleBitmap(&ccdc,rect.Width(),rect.Height());
		compatibledc.SelectObject(&bitmap);
		compatibledc.SelectObject(cbrush);
		compatibledc.BitBlt(0,0,rect.Width(),rect.Height(),&ccdc,0,0,SRCCOPY);//兼容位图跟普通不一样
	}
	yuanwenjiandc.SelectObject(cbrush);
	
	switch(m_leixing)
	{
	case 0:
		compatibledc.SetPixel(point,RGB(0,0,0));
		break;
		
	case 1:
		compatibledc.MoveTo(m_yuandian);
		compatibledc.LineTo(point);
		break;
		
	case 2:
		compatibledc.Rectangle(&CRect(m_yuandian,point));
		break;
		
	case 3:
		compatibledc.Ellipse(&CRect(m_yuandian,point));
		break;
	default:
		break;
	}


OnDraw中

	CRect rect;
	GetClientRect(&rect);
	pDC->BitBlt(0,0,rect.Width(),rect.Height(),&compatibledc,0,0,SRCCOPY);


  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
基于MFC(Microsoft Foundation Class)的绘图程序是一种在Windows平台上开发的应用程序,它使用MFC框架提供的各种类和函数来实现图形绘制功能。 绘图程序的主要功能包括绘制基本图形(如点、线、矩形、椭圆等)和编辑已有图形。在MFC中,可以使用CDC(Device Context)类的成员函数来绘制图形,如LineTo、Rectangle、Ellipse等。这些函数可以在指定的设备上进行绘制,比如屏幕或打印机。 此外,绘图程序还可以实现图形的选择、移动、缩放、旋转等操作。MFC提供了鼠标和键盘的消息响应函数,在用户进行交互操作时,可以捕获鼠标点击、拖拽等事件,并通过相应的处理函数来实现图形的编辑。 另外,绘图程序也可以支持图形的保存和加载。MFC提供了CFileDialog类,可以方便地让用户选择文件,并读写文件内容。通过保存和加载功能,用户可以将绘制的图形保存在文件中,下次打开时再进行编辑。 在实现绘图程序时,需要创建一个继承自CView的视图类,并使用MFC提供的文档视图模型(Document-View Model)来管理绘图数据和界面显示。视图类中的OnDraw函数被重写,实现具体的绘图操作,而处理用户交互的消息处理函数则放在视图类中的OnLButtonDown、OnLButtonUp等函数中。 总体来说,基于MFC绘图程序可以使用MFC提供的功能和类来实现图形的绘制、编辑和保存等操作,通过响应用户的交互事件,提供丰富的绘图功能,使用户能够方便地创建和编辑图形。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值