阅读本文前,我们假设您已经: 1,知道如何创建一个单文档的App Wizard 2,知道C++ 类、函数重载等简单知识 3,知道如何给View类或者Doc文档添加成员变量 4,会用MFC的IDE调试工具最好,那么本文的程序您可以copy去调试 我们在上一节讲了MFC框架App类、View类、MainFrame类和Doc类的关系,那么,基于消息的windows MFC程序设计是如何进行消息映射的呢? 在进行下一节之前,我们来复习一下。 对于一个MFC APPWizard运用程序,CMainFrame和View是窗口类,并且是父子关系,Doc一般用于数据的加载和存储,View用于图像的显示,App中是包括一些(窗口)初始化之类的东西。 好,我们开始这一节的内容。 一、 消息映射机制 windows程序设计是种事件驱动方式的程序设计,主要基于消息的。当用户需要完成某种功能时,需要调用OS某种支持,然后OS将用户的需要包装成消息,并投入到消息队列中,最后应用程序从消息队列中取走消息并进行响应。 在左边View类处点右键,在出现的菜单里点击“Add Windows Message Handler”,在出现的对话框里选择“WM_LBUTTONDOWN”,添加消息映射函数。 回到原文件,我们将看到三处进行了修改: 1, 在头文件(View.h)中声明消息响应函数原型。 //{{AFX_MSG(CDrawView) //注释宏 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG //注释宏 afx_msg宏表示声明的是一个消息响应函数。 2, 在源文件(View.cpp)中进行消息映射。 BEGIN_MESSAGE_MAP(CDrawView, CView) 在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。 宏ON_WM_LBUTTONDOWN()定义如下: #define ON_WM_LBUTTONDOWN() { WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown }, 3, 源文件中进行消息响应函数处理。 void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonDown(nFlags, point); } 说明: 可见当增加一个消息响应处理,在以上三处进行了修改。可在消息响应函数里添加消息处理代码完成对消息的响应、处理。
消息响应的可能方式: 1)在基类中针对每种消息做一个虚函数,当子类对消息响应时候,只要在子类中重写这个虚函数即可。缺点:MFC类派生层次很多,如果在基类对每个消息进行虚函数处理,那么从基类派生的每个子类都将背负一个庞大的虚表,这样浪费内存,故MFC没有采取这中方式而采取消息映射方式。 2)消息映射方式:MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里资源句柄(查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给基类,基类利用这个指针调用WindowProc()函数对消息进行处理,WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,由多态性知最总终调用子类的。在OnWndMsg()函数处理的时候,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明(从注释宏//{{AFX_MSG(CDrawView)...//}}AFX_MSG之间寻找),消息映射(从宏BEGIN_MESSAGE_MAP(...)....END_MESSAGE_MAP()之间寻找),最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。 说明: virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult); 二、 绘图DC的获取 说明:在View类添加WM_LBUTTONDOWN和WM_LBUTTONUP的消息处理函数,我们将用来演示各种DC的获取,以及显示效果。 View类添加全局变量CPoint m_ptOrigin用来存储左键按下点坐标。下面集中来关注OnLButtonUp中的绘图程序和效果。 以下语句添加于OnLButtonUp函数中,可以查看不同的效果。 1, 使用SDK获取DC句柄: HDC hdc; hdc=::GetDc(m_hWnd);//获取DC句柄 MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL); LineTo(hdc,point.x,point.y); ::ReleaseDC(m_hWnd,hdc);//释放DC 2, 利用CDC类指针和CWin类成员函数获取DC。 CDC *pDC=GetDC(); pDC->MoveTo(m_ptOrigin); pDC->LineTo(point); ReleaseDC(pDC); 3, 利用CClientDC对象。(CClientDC类从CDC类派生来的) CClientDC dc(this); dc.MoveTo(m_ptOrigin); dc.LineTo(point); 4, 利用CWindowDC对象。(CWindowDC类从CDC类派生来的) CWindowDC dc(this);// dc.MoveTo(m_ptOrigin); dc.LineTo(point); 5, 父窗口(MainFrame框架)和屏幕指针。 将上面的dc(this)分别改成GetParent()和GetDesktopWindow(),就可以得到父窗口指针和屏幕窗口指针。 可以分别试验画线效果。 6, 利用画笔改变线条颜色和类型: 7, 使用画刷(通常利用画刷去填充矩形区域): 使用单色画刷 CBrush brush(RGB(255,0,0));//构造画刷对象 CClientDC dc(this); dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的画刷去填充矩形区域
使用位图画刷 CBitmap bitmap;//构造位图对象(使用前需要初试化) bitmap.LoadBitmap(IDB_BITMAP1);//初试化位图对象 CBrush brush(&bitmap);//构造位图画刷 CClientDC dc(this); dc.FillRect(CRect(m_ptOrigin,point),&brush);//用指定的位图画刷去填充矩形区域
使用透明画刷 CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//获取透明画刷对象指针 CClientDC dc(this); CBrush *pOldBrush=dc.SelectObject(pBrush);//将透明画刷选入DC dc.Rectangle(CRect(m_ptOrigin,point)); dc.SelectObject(pOldBrush);//释放透明画刷 8, 注意点: 1)静态方法不属于某一个具体对象,而属于类本身,在类加载的时候就已经为类静态方法分配了代码去,故可用CBrush::FromHandle()形式调用。 2)静态方法中,不能引用非静态的数据成员和方法。 3)静态数据成员需要在类外单独做初始化,形式如: 变量类型 类名::变量名=初始值;
|
欢迎以任何形式转载本文,只要对您有用
|
韦伯主页: http://mail.ustc.edu.cn/~bywang(提供此笔记系列相关源程序下载) 韦伯Blog: http://webbery.tianyablog.com |
参考书目和网站: |
(1)孙鑫VC++视频 |
(2)1-6章主要参考: hbyufan的BLog |
(3)11-20章主要参考: songpeng的Blog(写此文的时候打不开此blog) |