您好,欢迎访问三七文档
当前位置:首页 > 建筑/环境 > 工程监理 > mfc消息映射机制及绘制曲线
MFC消息映射机制的剖析学会使用ClassWizard理解发送给窗口的消息是如何被MFC框架窗口类的函数进行响应的。掌握设备描述表及其封装类CDC的使用,CDC是如何与具体的设备发生关联的;新建一个单文档应用程序,在窗口上实现画线在窗口上画线,首先要有2个点,这2个点如何去捕获?Windows应用程序是基于消息的编程当我们用鼠标在窗口中点击一下,这时就有了一个点,这个点就可以作为直线的起点。所以可以去捕获一个鼠标左键按下的消息,在这个消息响应中获取一个点。按住鼠标左键拖动然后释放左键,这时也有一个点,这个点就是直线的终点。也就是说我们要捕获2个消息,一个是鼠标左键按下的消息,一个是鼠标左键释放的消息。有了这2个消息,对这2个消息进行响应,在响应的过程中可以得到起点和终点。哪个窗口响应鼠标消息呢?对于文档视图结构来说,有视图窗口和框架窗口,视图窗口始终是覆盖在框架之上的,就好像说有一面墙作为框架窗口,有一面墙它始终挡在框架窗口这面墙前面,你对框架窗口这么墙的操作始终是对它前面墙的操作,窗口也是一样,就是在框架窗口中捕获不到任何鼠标的消息。包括鼠标点击、鼠标移动。MFC消息的路由在SDK中讲过:一旦消息产生,操作系统会把消息放入消息队列中,应用程序通过GetMessage从消息队列中取出一条消息,然后DispatchMessage交给操作系统,操作系统会调用窗口过程函数去进行处理。而在MFC中,好像不是走的这条路线。只要想捕获消息,增加消息响应函数,遵照这个步骤,就可以完成消息的响应。并不是象以前的switch~case对想捕获的消息进行处理。MFC消息的路由在MFC中,采用的是消息映射的方式。在我们的程序中。MFC在后台维护了一个句柄和c++对象的指针的对照表,也就是说,和视图类相关的有个窗口,窗口肯定有个窗口句柄,这个窗口句柄就和我们的视图类的指针对照起来。当我们收到消息的时候,消息的第一个参数就是窗口句柄,也就是说这个消息和哪个窗口相关的。通过这个句柄我们就可以找到与它相关联的c++对象的指针,然后通过这个指针传递给基类,基类通过消息循环会去调用函数WindowProc来对消息进行处理。typedefstructtagMSG{HWNDhwnd;UINTmessage;WPARAMwParam;LPARAMlParam;DWORDtime;POINTpt;}MSG;MFC消息的路由而WindowProc是个虚函数。也就是说,当子类继承CWnd时,它在后台都会有这样一个函数。在这个函数内部,调用OnWndMsg。消息的路由处理就是由它来完成的,在这个函数里完成对消息的映射的处理。我们知道消息中的窗口句柄是和一个c++对象的指针相关联的,通过这个指针传给基类,这样的话它调用的都是子类的OnWndMsg。在这个函数里,看看是否有消息响应函数MFC消息的路由就是在类的头文件中看DECLARE_MESSAGE_MAP()这个宏前面是否有消息响应函数的原型声明,然后在源文件中BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()这2个宏之间是否有消息响应的映射的宏。如果通过这2个步骤找到了消息响应函数,那么就会对这个消息进行处理。这就是MFC消息映射的机制。完成画线的功能方法一:用API函数在MFC中如果要调用SDK的函数的话,要加::前缀hdc=::GetDC(m_hWnd);MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL);LineTo(hdc,point.x,point.y);::ReleaseDC(m_hWnd,hdc);从CWnd派生出来的类中都有一个数据成员m_hWnd保存了跟C++类对象相关的窗口句柄完成画线的功能方法二:用CDC类有一个数据成员m_hDC,它保存设备描述表,也就是与CDC这个类相关的句柄,就像m_hWnd保存窗口句柄一样。CDC*pDC=GetDC();/*CWnd中的成员函数*/pDC-MoveTo(m_ptOrigin);pDC-LineTo(point);ReleaseDC(pDC);/*CWnd类的成员函数*/完成画线的功能方法三:用CClientDC类在构造函数中会调用GetDC得到句柄,在析构函数中会调用ReleaseDC释放句柄。所以不需要显示调用GetDC和ReleaseDC,只需要构造CClientDC类的一个对象即可。当这个对象的生命周期结束时,资源会释放掉。CClientDCdc(this);/*作图是在与视图类相关的窗口上,如何传递视图类的指针?每个类都有一个隐含的this,指向类对象本身。*/dc.MoveTo(m_ptOrigin);dc.LineTo(point);完成画线的功能框架窗口的客户区是从工具栏开始,往下。非客户区就是标题栏和菜单栏,而视图(View)窗口是没有非客户区的。作图都是在客户区作图。在框架窗口的客户区如何画图?框架窗口是视图窗口的父窗口将CClientDCdc(this);替换为CClientDCdc(GetParent());完成画线的功能方法四:用CWindowDC类好处:可访问窗口的整个区域,包括客户区和非客户区。(1)CWindowDCdc(this);(2)dc.MoveTo(m_ptOrigin);(3)dc.LineTo(point);和CClientDC有没有区别?换成CWindowDCdc(GetParent());编译运行,结果如何?画线可以访问整个框架窗口区域。包括客户区和非客户区。所以用CWindowDC,就可以在非客户区作图。完成画线的功能能不能画到整个屏幕上面?是可以的,关键是看DC是与哪个窗口相关联。实际上我们的桌面本身就是一个窗口。所以如果我们获取一个与桌面相关联的一个CDC的对象,就可在桌面上画图。要获取桌面,用GetDesktopWindow函数。CWindowDCdc(GetDesktopWindow());完成画线的功能实现连续画线的功能曲线是由许多连续的小线段组成,要把这些点都捕获到,则需要捕获鼠标移动的消息。(1)为视图捕获鼠标移动的消息,OnMouseMove。只要鼠标移动就会进到这个消息处理函数来。这不是我们所期望的,我们希望按下鼠标左键才作图。所以我们要判读什么时候按下了鼠标左键。设置一个布尔变量,按下了鼠标左键设置当真,当释放鼠标左键时设置为假。在构造函数中初始化为假。完成画线的功能实现连续画线的功能(2)在OnMouseMove中CClientDCdc(this);if(m_bDraw==TRUE){dc.MoveTo(m_ptOrigin);dc.LineTo(point);m_ptOrigin=point;}运行,就可以实现写汉字功能。即按下鼠标左键可以实现连续画线。完成画线的功能画扇形在鼠标移动事件中,CClientDCdc(this);if(m_bDraw==TRUE){dc.MoveTo(m_ptOrigin);dc.LineTo(point);}完成画线的功能带边线的扇形要增加一个新的变量,是点,是个旧的点,设为私有。CPointm_ptOld;在构造函数中设为0。当鼠标左键按下时,把鼠标的点分别赋给这2个点。if(m_bDraw==TRUE){dc.MoveTo(m_ptOrigin);dc.LineTo(point);dc.MoveTo(m_ptOld);dc.LineTo(point);m_ptOld=point;}完成画线的功能实现画笔中画线的功能,就是鼠标移动过程中画一系列临时的直线,鼠标左键释放时画一条永久直线。【作图模式及应用】SetRop2可设置作图模式作图模式R2_NOT下画的直线是通过反转屏幕当前颜色作出的;在某一位置首次画一条直线时,它是可见的,但是在相同的位置第二次画直线时,就变为不可见的。完成画线的功能实现画笔中画线的功能,就是鼠标移动过程中画一系列临时的直线,鼠标左键释放时画一条永久直线。在OnMouseMove中if(m_bDraw==TRUE){dc.SetROP2(R2_NOT);dc.MoveTo(m_ptOrigin);dc.LineTo(m_ptOld);dc.MoveTo(m_ptOrigin);dc.LineTo(point);m_ptOld=point;}窗口中图形的保存和重绘窗口重绘时,调用OnDraw函数的机制窗口重绘时会发送WM_PAINT消息,OnDraw函数并不是WM_PAINT消息响应函数,那么为什么窗口重绘时它会被调用呢?分析基类CView中响应WM_PAINT的消息响应函数voidCView::OnPaint(){//standardpaintroutineCPaintDCdc(this);OnPrepareDC(&dc);OnDraw(&dc);}窗口重绘时,调用OnDraw函数的机制是因为当窗口重绘时,会发送WM_PAINT消息,而我们CMiniDrawView类中没处理这个消息,会回到基类的消息处理函数OnPaint中,在这个函数中调用OnDraw函数。给我们的感觉就好像是OnDraw函数专门用来负责重绘窗口的。跟在SDK编程中讲过的窗口重绘过程是一致的,只是将重绘的过程封装成几个函数了,在OnPaint中调用OnDraw。这个函数的目的是利用这个函数给程序员提供一种方便,可以在这进行图形的绘制。当窗口尺寸发生变化时,会引起窗口的重绘,发送WM_PAINT消息。首先是擦除窗口背景,然后引起重绘。如果要让图形始终在窗口中出现,就需要将绘制的图形保存起来。当窗口尺寸发生变化时,再将这个图形在窗口中绘制出来。在窗口中输出图形,可以在OnDraw函数中实现。因为窗口重绘时,会调用OnDraw函数。那如何保存图形呢?在C++中,结构体就是一个类。直线的要素是起点和终点。所以可以用一个类的对象来保存图形的两要素,这也符合面向对象的程序设计的特点。•利用菜单Insert插入一个新的类,名字叫CLine,类的类型是Generic类而不是MFC类。•在类中增加2个成员变量,设为共有的。CPointm_ptOrigin,m_ptEnd;•构造一个CLine的对象就可以保存直线的2要素,提供带参数的构造函数。CLine::CLine(CPointm_ptOrigin,CPointm_ptEnd){this-m_ptOrigin=m_ptOrigin;this-m_ptEnd=m_ptEnd;}用什么来保存这些直线呢?数组?链表?集合类CPtrArray固定大小操作复杂利用CPtrArray去存储CLine对象的地址(1)在视图类中定义变量CPtrArraym_ptArray;(2)在OnLButtonUp中CLineline(m_ptOrigin,point);m_ptArray.Add(&line);(3)在OnDraw中,将m_ptArray中保存的图形取出来重画intnCount=m_ptArray.GetSize();for(inti=0;inCount;i++){pDC-MoveTo(((CLine*)m_ptArray.GetAt(i))-m_ptOrigin);pDC-LineTo(((CLine*)m_ptArray.GetAt(i))-m_ptEnd);}当窗口尺寸发生变化时,图形没有在窗口中再次出现?在OnLButtonUp函数中0088:4400②CLineline(…);CLine的对象在栈中的内存0088:4400①CPtrArraym_ptrArray;③m_ptrArray.Add(&line)CLine的对象发生析构,内存被回收。如何解决这个问题呢?在OnLButtonUp函数中1244:EE00CLine的对象在堆中的内存①CPtrArraym_ptrArray;②CLine*pLine;0088:46601244:EE001244:EE00pLine的内存④m_ptrArray.Add(pLine);③pLin
本文标题:mfc消息映射机制及绘制曲线
链接地址:https://www.777doc.com/doc-3970412 .html