MFC动态画线,画圆,画椭圆,画矩形

今天我在csdn网站上通过学习别人的博客学习到了动态画矩形,画线的方法,这使得我对Windows的消息循环机制有了更深刻的理解。首先来总结一下我今天的收获,呵呵有收获就是不错的,说明我进步了,感谢这两篇博文:

VC鼠标拖动动态画矩形框(http://blog.csdn.net/zhouxuguang236/article/details/7686163) 和 VC动态轨迹画线 (http://blog.csdn.net/akof1314/article/details/5547616),更感谢转载前一篇博文的作者(zhouxuguang236),是他给了我第二个博文的链接,使得我不用自己去搜索并尝试哪一篇的博文更便于我的学习。

好了下面开始言归正传:

所谓的动态画线,画矩形,画椭圆的本质就是在于消隐问题的解决以及对Windows消息循环机制的理解,消隐问题:因为MFC中有一个函数SetROP2,通过将该函数的参数设置为R2—NOT(当前绘制的像素值设为屏幕像素值的反色,这里面的“屏幕”二字是指你所绘制的图形所占据的那一部分屏幕区域,即直线所占的屏幕区域就是直线所在的那一段线的区域),利用这一点通过在同一区域重复画两次便可以将该区域的形状消隐。

而Windows消息循环机制更好的体现于MouseMove消息响应中,因为要实现动态画线、画矩形这就体现在鼠标在绘图区的移动上,鼠标在绘图区移动时,产生动态的效果,也就是说在mouse 移动的过程中始终在画线、画矩形,这就要求在MouseMove消息响应中有画线、画矩形的相应函数的调用,但正是因为mouse移动的过程中画了很多的线、矩形,所以我们就要在mouse移动的过程中将之前画出的线、矩形消隐掉,才能保证实现了自己所要实现的功能——动态画线、画矩形,但又没有产生多余的线和矩形。

消隐的问题和动态画图的问题关键在于MouseMove消息响应中的函数调用的顺序:

1.首先将上次所画的图形消隐掉

2.画出临时的图形

3.消息循环的机制:循环执行上述代码(这是Windows循环的机制,不需要自己代码实现)

在最后的LButtonUp消息响应中,需要将上面的MouseMove消息响应中循环的最后一次中最后所画的临时图形消隐掉,之后根据LButtonUp消息响应中point参数画出最终的图形。综上可知:MouseMove中的代码和LButtonUp中消隐临时图形的代码所要实现的功能就是动态这一过程,而图形的最终绘制还在与LButtonUp消息响应及其point参数。

代码如下:

  1. private:
private:
  1. //......
  2. HCURSOR m_HCross;
  3. UINT m_drawType;
  4. CPoint m_OldPoint;
  5. CPoint m_startPoint;
  6. BOOL m_startRect;
//......
	HCURSOR m_HCross;
	UINT m_drawType;
	CPoint m_OldPoint;
	CPoint m_startPoint;
	BOOL m_startRect;
  1. CGISView::CGISView()
  2. {
  3. // TODO: add construction code here
  4. //....
  5. m_startRect=FALSE;
  6. m_startPoint=0;
  7. m_OldPoint=0;
  8. m_drawType=0;
  9. //初始化m_HCross为十字光标
  10. m_HCross=AfxGetApp()->LoadStandardCursor(IDC_CROSS);
  11. }
CGISView::CGISView()
{
	// TODO: add construction code here
	//....
	m_startRect=FALSE;
	m_startPoint=0;
	m_OldPoint=0;
	m_drawType=0;
	//初始化m_HCross为十字光标
	m_HCross=AfxGetApp()->LoadStandardCursor(IDC_CROSS);

}
  1. void CGISView::OnLButtonDown(UINT nFlags, CPoint point)
  2. {
  3. // TODO: Add your message handler code here and/or call default
  4. m_startRect=TRUE; //鼠标左键单击,设置可以开始绘制矩形框
  5. m_startPoint=point; //记录起始点
  6. m_OldPoint=point;//设置老点也为起始点
  7. //设置光标为十字光标
  8. ::SetCursor(m_HCross);
  9. CView::OnLButtonDown(nFlags, point);
  10. }
void CGISView::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	m_startRect=TRUE; //鼠标左键单击,设置可以开始绘制矩形框
	m_startPoint=point; //记录起始点
	m_OldPoint=point;//设置老点也为起始点
	//设置光标为十字光标
	::SetCursor(m_HCross);


	CView::OnLButtonDown(nFlags, point);
}
  1. void CGISView::OnMouseMove(UINT nFlags, CPoint point)
  2. {
  3. // TODO: Add your message handler code here and/or call default
  4. CClientDC dc(this); //获取设备句柄
  5. dc.SetROP2(R2_NOT);//此为关键
  6. dc.SetROP2(R2_NOT);//所绘制的图形并没有消失,所以可以验证下面的连续两次颜色取反不是在一次相应中完成
  7. //SetRop2 Specifies the new drawing mode.(MSDN)
  8. //R2_NOT Pixel is the inverse of the screen color.(MSDN)
  9. //即:该函数用来定义绘制的颜色,而该参数则将颜色设置为原屏幕颜色的反色
  10. //这样,如果连续绘制两次的话,就可以恢复原来屏幕的颜色了(如下)
  11. //但是,这里的连续两次绘制却不是在一次消息响应中完成的
  12. //而是在第一次拖动响应的绘制可以显示(也就是看到的),第二次拖动绘制实现擦出(也就看不到了)
  13. dc.SelectStockObject(NULL_BRUSH);//不使用画刷
  14. if(TRUE==m_startRect) //根据是否有单击判断是否可以画矩形
  15. {
  16. switch(m_drawType)
  17. {
  18. case 1://Rectangle
  19. ::SetCursor(m_HCross);
  20. dc.Rectangle(CRect(m_startPoint,m_OldPoint));
  21. dc.Rectangle(CRect(m_startPoint,point));
  22. m_OldPoint=point;
  23. break;
  24. case 2: //Line
  25. ::SetCursor(m_HCross);
  26. //擦去上一次绘制的临时线
  27. dc.MoveTo(m_startPoint);
  28. dc.LineTo(m_OldPoint);
  29. //绘制这一次的临时线
  30. dc.MoveTo(m_startPoint);
  31. dc.LineTo(point);
  32. //将临时线的终点复制给m_OldPoint,
  33. //使其在消息循环的过程中将该值传递到
  34. //擦去上一次画线的过程中,以便擦去上一次所画的线
  35. m_OldPoint=point;
  36. break;
  37. case 3: //Circle
  38. ::SetCursor(m_HCross);
  39. //擦去上一次绘制的临时圆
  40. //设定该圆的y坐标,因为要保证两点的x之差等于y之差
  41. m_OldPoint.y=m_OldPoint.x-m_startPoint.x+m_startPoint.y;
  42. dc.Ellipse(CRect(m_startPoint,m_OldPoint));
  43. //绘制临时圆
  44. point.y=point.x-m_startPoint.x+m_startPoint.y;
  45. dc.Ellipse(CRect(m_startPoint,point));
  46. m_OldPoint=point;
  47. break;
  48. case 4: //Ellipse
  49. ::SetCursor(m_HCross);
  50. dc.Ellipse(CRect(m_startPoint,m_OldPoint));
  51. dc.Ellipse(CRect(m_startPoint,point));
  52. m_OldPoint=point;
  53. break;
  54. case 5: //Dot
  55. break;
  56. }
void CGISView::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	
	CClientDC dc(this); //获取设备句柄
	dc.SetROP2(R2_NOT);//此为关键
	dc.SetROP2(R2_NOT);//所绘制的图形并没有消失,所以可以验证下面的连续两次颜色取反不是在一次相应中完成
	//SetRop2 Specifies the new drawing mode.(MSDN)
         //R2_NOT   Pixel is the inverse of the screen color.(MSDN)
         //即:该函数用来定义绘制的颜色,而该参数则将颜色设置为原屏幕颜色的反色
	//这样,如果连续绘制两次的话,就可以恢复原来屏幕的颜色了(如下)
	//但是,这里的连续两次绘制却不是在一次消息响应中完成的
	//而是在第一次拖动响应的绘制可以显示(也就是看到的),第二次拖动绘制实现擦出(也就看不到了)

	dc.SelectStockObject(NULL_BRUSH);//不使用画刷
	if(TRUE==m_startRect) //根据是否有单击判断是否可以画矩形
	{
		switch(m_drawType)
		{
       	 	     case 1://Rectangle

				::SetCursor(m_HCross);
	        			dc.Rectangle(CRect(m_startPoint,m_OldPoint));
	                   	dc.Rectangle(CRect(m_startPoint,point));
				m_OldPoint=point;
			
				break;

		     case 2: //Line
				::SetCursor(m_HCross);
				//擦去上一次绘制的临时线
				dc.MoveTo(m_startPoint);
			         dc.LineTo(m_OldPoint);   
				
				//绘制这一次的临时线
			           dc.MoveTo(m_startPoint);
				dc.LineTo(point);

				//将临时线的终点复制给m_OldPoint,
				//使其在消息循环的过程中将该值传递到
				//擦去上一次画线的过程中,以便擦去上一次所画的线
				m_OldPoint=point;
				


				break;
		     case 3:  //Circle
				::SetCursor(m_HCross);
	                           //擦去上一次绘制的临时圆
				//设定该圆的y坐标,因为要保证两点的x之差等于y之差
				m_OldPoint.y=m_OldPoint.x-m_startPoint.x+m_startPoint.y;
				dc.Ellipse(CRect(m_startPoint,m_OldPoint));

				//绘制临时圆

				point.y=point.x-m_startPoint.x+m_startPoint.y;
				dc.Ellipse(CRect(m_startPoint,point));

				m_OldPoint=point;
				break;
		     case 4:  //Ellipse
				::SetCursor(m_HCross);

				dc.Ellipse(CRect(m_startPoint,m_OldPoint));
				dc.Ellipse(CRect(m_startPoint,point));
				m_OldPoint=point;
				break;
		     case 5:   //Dot

				break;

	}
        }
}
  1. void CGISView::OnLButtonUp(UINT nFlags, CPoint point)
  2. {
  3. // TODO: Add your message handler code here and/or call default
  4. m_startRect=FALSE;//重置绘制矩形框标志
  5. ::ClipCursor(NULL);//解锁光标,即将光标恢复为默认光标
  6. //消隐最后的一个矩形
  7. CClientDC dc(this);//获取设备句柄
  8. // dc.SetROP2(R2_NOT); //在MouseMove消息响应中使用过该函数了,所以在这里再一次使用会使得其恢复为屏幕的颜色
  9. dc.SelectStockObject(NULL_BRUSH); //设置画刷为空画刷
  10. switch(m_drawType)
  11. {
  12. case 1: //Retangle
  13. //利用当前画刷绘制矩形,内部由当前空画刷(NULL_BRUSH)填充。
  14. //擦去MouseMove消息响应中的临时矩形
  15. dc.Rectangle(CRect(m_startPoint,m_OldPoint));
  16. //绘制固定矩形
  17. dc.Rectangle(CRect(m_startPoint,point));
  18. //m_drawType=0; //该行的可以使得每画一次都要选择所画的形状
  19. break;
  20. case 2: //Line
  21. //擦去MouseMove消息响应中绘制的最后一次临时线
  22. dc.MoveTo(m_startPoint);
  23. dc.LineTo(m_OldPoint);
  24. //绘制固定线
  25. dc.MoveTo(m_startPoint);
  26. dc.LineTo(point);
  27. break;
  28. case 3: //Circle
  29. dc.Ellipse(CRect(m_startPoint,m_OldPoint));
  30. dc.Ellipse(CRect(m_startPoint,point));
  31. break;
  32. case 4: //Ellipse
  33. dc.Ellipse(CRect(m_startPoint,m_OldPoint));
  34. dc.Ellipse(CRect(m_startPoint,point));
  35. break;
  36. case 5: //Dot
  37. dc.SetPixel(point,RGB(0,0,0));
  38. break;
  39. }
  40. CView::OnLButtonUp(nFlags, point);
  41. }
void CGISView::OnLButtonUp(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	m_startRect=FALSE;//重置绘制矩形框标志
	::ClipCursor(NULL);//解锁光标,即将光标恢复为默认光标
	//消隐最后的一个矩形
	CClientDC dc(this);//获取设备句柄
//	dc.SetROP2(R2_NOT); //在MouseMove消息响应中使用过该函数了,所以在这里再一次使用会使得其恢复为屏幕的颜色
	dc.SelectStockObject(NULL_BRUSH);  //设置画刷为空画刷
    switch(m_drawType)
	{
 	case 1: //Retangle

		//利用当前画刷绘制矩形,内部由当前空画刷(NULL_BRUSH)填充。
		//擦去MouseMove消息响应中的临时矩形
		dc.Rectangle(CRect(m_startPoint,m_OldPoint));
		//绘制固定矩形
		dc.Rectangle(CRect(m_startPoint,point));

		//m_drawType=0;  //该行的可以使得每画一次都要选择所画的形状
 	          break;

          case 2:  //Line

		//擦去MouseMove消息响应中绘制的最后一次临时线
		 dc.MoveTo(m_startPoint);
                   dc.LineTo(m_OldPoint);
		 
		 //绘制固定线
		 dc.MoveTo(m_startPoint);
		 dc.LineTo(point);
 		break;
 	 case 3:  //Circle
 
		dc.Ellipse(CRect(m_startPoint,m_OldPoint));
		dc.Ellipse(CRect(m_startPoint,point));
 		break;
 	 case 4:  //Ellipse

		dc.Ellipse(CRect(m_startPoint,m_OldPoint));
		dc.Ellipse(CRect(m_startPoint,point));
 		break;
 	 case 5:  //Dot
                  dc.SetPixel(point,RGB(0,0,0));
 		break;
	
	}
	CView::OnLButtonUp(nFlags, point);
}
  1. void CGISView::OnRectangle()
  2. {
  3. // TODO: Add your command handler code here
  4. m_drawType=1;//设置所画的为矩形
  5. }
  6. void CGISView::OnLine()
  7. {
  8. // TODO: Add your command handler code here
  9. m_drawType=2;
  10. }
  11. void CGISView::OnEllipse()
  12. {
  13. // TODO: Add your command handler code here
  14. m_drawType=4;
  15. }
  16. void CGISView::OnDot()
  17. {
  18. // TODO: Add your command handler code here
  19. m_drawType=5;
  20. }
  21. void CGISView::OnCircle()
  22. {
  23. // TODO: Add your command handler code here
  24. m_drawType=3;
  25. }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值