Windows编程之窗口和MFC

学习是常新的,学习又是重复的。从C到C++,再到MFC,补充STL,来来回回趟浑水,感觉类似于小偷爬墙,可惜遇到了故宫的高墙,最后没办法只好通过下水道爬出去。可惜学习却没有下水道,只能不断地向上爬。。。

基础知识点:
(1)创建一个窗口;
(2)了解MFC类的体系;
(3)发消息到一个窗口;
(4)在窗口内绘图。

1. 窗口

1.1 什么是窗口,都有哪些窗口?
窗口是屏幕上一个区域,应用程序在该区域内显示数据并等待用户操作。
有三类窗口:重叠窗口、弹出窗口、子窗口,它们之间的区别如下:
重叠窗口与弹出窗口:弹出窗口可以没有标题栏
子窗口与重叠/弹出窗口:子窗口只能出现在另一个窗口中,且被该窗口裁剪,此外子窗口不能有菜单条。
*客户区(由应用程序绘制)和非客户区(由系统绘制)
*窗口类—〉窗口对象—〉(被)窗口管理器(管理)

1.2 Windows API与MFC
*Windows API提供了一个应用程序接口用于创建和维护窗口对象,如:
        CreateWindow, SetWindowLong, MoveWindow, DestroyWindow
*恩哼,事情全让Windows API做了,要MFC何用呢?MS不过是用C++技术对Windows API进行了封装形成MFC,从而可以减轻一些乏味的编码工作。如何创建一个MFC窗口:创建CWnd类的一个实例,调用该类的成员函数(该函数则调用 API中的CreateWindow函数),返回的窗口句柄保存在CWnd的成员变量m_hWnd中。需要销毁窗口时,必须确保销毁了窗口对象,及其 CWnd实例。
        CWnd wnd; //声明了一个CWnd类的对象
        BOOL b = wnd.CreateEx( ExStyle, ClassName, WindowName, Style, x, y, Width, Height, Parent, Menu, Param); //创建真正的窗口

1.2.1 使用MFC创建一个窗口类
先创建WNDCLASS结构的实例,然后用MFC类库的AfxRegisterClass()注册它。也可以用MFC类库的AfxRegisterWndClass()来创建一个基于调用参数的WNDCLASS对象。
        LPCTSTR lpszClassName=AfxRegisterWndClass(UINT nClassStyle, HCURSOR hCursor=0, HBRUSH hbrBackground=0, HICON hIcon=0);

1.2.2 由系统提供的窗口类
        弹出式菜单窗口
        桌面窗口
        对话框
        MDI子窗口区域

        BUTTON 按钮控件窗口
        SCROLLBAR 滚动条控件窗口
        STATIC 静态控件窗口
        COMBOBOX 组合框控件窗口
        EDIT 编辑控件窗口
        ComboLBox 列表框控件窗口

        RICHEDIT 多信息编辑控件窗口
        SysTabControl32 选项卡控件窗口
        SysListView32 列表视图控件窗口
        SysMonthCal32 月历控件窗口
        ComboBoxEx32 扩展组合框控件窗口
        SysDateTimePick32 日期/时间选项控件窗口
        SysAnimate32 动画控件窗口
        msctls_hotkey32 热键控件窗口
        msctls_trackbar32 幻灯片控件窗口
        tooltips_class32 工具提示控件窗口
        SysTreeView32 树型视图控件窗口
        msctls_statusbar32 状态栏窗口
        msctls_updown32 微调按钮控件窗口
        toolbarWindow32 工具栏窗口
        msctls_progress32 进度指示控件窗口
        ReBarWindow32 Rebar窗口
        SysHeader32 标题控件窗口

1.2.3 MFC提供的窗口类
        AfxWnd CWnd窗口
        AfxMDIFrame MDI框架窗口
        AfxFrameOrView MFC框架和视窗
        AfxControlBar MFC控制条窗口

2. MFC类库
主要包括:
*访问用户界面的类,包括CWnd。
*帮助绘图的类。
*提供运行一个应用程序所需功能的类。
*处理数组和数据列表的类。
*访问数据库的类。
*维护文件的类。
*允许应用程序在网上或Internet上通信的类。
*一些用来帮助同步和调试应用程序的混合类。

2.1 三个基类(从上到下继承)
*CObject:从CObject派生的类,具有在运行时获得对象大小和名字的能力
*CCmdTarget:从CCmdTarget派生的类,能够处理命令消息
*CWnd:从CWnd派生的类,能控制它们自己的窗口
        CObject——〉CCmdTarget——〉CWnd

2.1.1 CObject----六个宏
*DECLARE_DYNAMIC(CYourClass)//inthe.hfile
*IMPLEMENT_DYNAMIC(CYourClass,CYourBaseClass)//inthe.cppfile

*DECLARE_DYNCREATE(CYourClass)//inthe.hfile
*IMPLEMENT_DYNCREATE(CYourClass,CYourBaseClass)//inthe.cppfile

*DECLARE_SERIAL(CYourClass)//inthe.hfile
*IMPLEMENT_SERIAL(CYourClass,CYourBaseClass,schema)//inthe.cppfile

2.2 应用程序类——负责初始化和运行应用程序
应用程序类CWinAPP(O/C/W)是应用程序开始后创建的第一个对象,并且是在结束前最后一个执行的对象。起动时,应用程序类负责创建应用程序的其余对象。
(1) 对于一个对话框应用程序,应用程序类应用CDialog创建一个对话框。
(2) 对于一个SDI应用程序,应用程序类创建一个或多个文档模板,然后用模板打开一个空文档。
(3) 对于一个MDI应用程序,在主框架类中,应用程序类创建一个或多个文档模板,然后用模板打开一个空文档。
应用程序类也同操作系统交互,作为中继,将鼠标信息和键盘输入传给应用程序的其余部分。

2.2.1 文档模板
应用程序打开一个文档时,文档模板定义创建什么样的框架、文档和视图。要创建一个文档模板,要么为SDI应用程序创建一个CSingleDocTemplate类,要么为MDI应用程序创建CMultiDocTemplate类,并且用三个类的指针对它进行初始化。
pDocTemplate=new CMultiDocTemplate(IDR_APPTYPE,
        RUNTIME_CLASS(CAppDoc), //YourDocumentClass
        RUNTIME_CLASS(CChildFrame), //YourFrameClass
        RUNTIME_CLASS(CAppView)); //YourViewClass
这里的RUNTIME_CLASS宏返回一个指向类的CRuntimeClass结构的指针,该结构用DECLARE_DYNCREATE和 IMPLEMENT_DYNCREATE宏填加到类中。文档模板通过用CRuntimeClass::CreateObject()函数创建一个所有三个 类的实例来打开一个文档。

2.2.2 线程
CWinApp类自身是从CWinThread派生的。CWinThread类封装了系统中用来创建和维护应用程序线程的WindowsAPI函数。实际上,可以通过创建CWinThread类的另一个实例实现应用程序多任务。CWinApp类代表主要的执行线程。

2.2.3 CFrameWnd(O/C/W)
框架类CFrameWnd(O/C/W)是应用程序运行时创建的第二个对象,负责显示和监督用户对应用程序其余部分的命令。对于一个SDI应用程序,框架类是从CFrameWnd派生的,AppWizard自动命名为:CMainFrame。
对于一个MDI应用程序,框架类派生于CMDIFrameWnd,AppWizard自动命名为CMainFrame。在MDI应用程序中,每个打开的文 档有一个子框架类,每个子框架类是从CMDIChildWnd派生的。AppWizard自动指定子框架类名为CChildFrm。对于一个对话框应用程 序来说,没有框架类。

2.2.4 CDocument(O/C)
通常,文档类CDocument(O/C)是应用程序打开一个新文档或一个已存在的文档时创建的对象。文档类负责将一个文档赋给它的成员变量,并允许视图 类编辑这些成员变量。一个文档包括从图形文件到可编程控制的任何内容。文档类派生于CDocumnt,AppWizard自动命名为CXxxDoc, Xxx是应用程序名。

2.2.5 CView(O/C/W)
在创建文档类的一个实例之后将创建视图类CWiew(O/C/W)的一个实例。视图类负责描述文档类的内容,它也可以允许用户编辑文档。窗口分区类,即 SplitterWnd,允许文档有多视图;这些视图以由相同视图类的一些实例创建,也可以由完全不同的视图类实例创建。AppWizard允许视图类从 几个基类(包括CTreeView、CEditView、CRichEditView、CListView等)之一派生,每一个基类赋予应用程序不同的功 能集。所有这些类都派生于CView类,不管选择哪个基类,AppWizard自动命名派生类为CXxxView,这里的Xxx是应用程序名。

2.2.6 对话框应用程序
(a) 系统创建应用程序实例;(b) 应用程序类创建对话框类的实例;(c) 对话框类创建其他控件。

2.2.7 单文档界面应用程序
单文档界面应用程序包括以下类:从CWinApp派生的应用程序类、从CFrameWnd派生的框架类、从CDocument派生的文档类,以及每个文档一个或多个视图类,这些视图类是从一些派生于CView的视图类派生的。
①系统创建应用程序类的实例;②应用程序类创建一个文档模板,并告知它装入一个空的文档类;③接着,文档模板类创建文档、框架和视图类的一个实例;④框架和视图类在屏幕上显示窗口。

2.2.8 多文档界面应用程序(MDI)
MDI程序包括:派生于CWinApp的应用程序类、派生于CMDIFrameWnd的框架类、派生于CMDIChildWnd的一个或多个子框架类、派生于CDocument的每个子框架一个文档类和派生于CView的每个文档一个或多个视图类。

2.3 其他用户界面类
*通用控件类
*菜单类
*对话框类
*控制条类(CToolBar、CStatusBar、CDialogBar、CRebar)
*属性类

2.4 绘图类
*设备环境类:CDC、CClientDC、CWindowDC、CPaintDC、CMetaFileDC
*图形对象类:CBitmap, CBrush, CFont, CPalette, CPen, CRgn

2.5 文件类
*CFile, CMemFile, CSharedFile, CStdioFile, CFileFind
*CArchive和串行化

2.6 数据库类
*开放数据库连接(ODBC):封装了大多数数据库厂商支持的ODBC API。如果应用程序使用了MFC的ODBC类,它就可以支持任何支持ODBC标准的数据库管理系统(DBMS)。
*数据访问对象(DAO):支持一个更新的数据库API,这已被Microsoft Jet数据库引擎有效利用。也可以通过Jet引擎访问符合ODBC的数据库系统和其他数据源。

2.7 集合类:CArray、CList、CMap

2.8 其他数据类:CTime和CTimeSpan类、COleDateTime和COleDateSpan类、ColeCurrency类、CString类、CPoint类、CSize类、CRect类

2.9 通信类
*网络类(Network Classes):CASyncSocket、CSocket
*因特网类
*因特网客户类(Client Classes):封装了Win32 Internet Extensions(WinInet),允许用
C++语言利用四种协议之一对因特网进行访问,这四种协议为:文件传输协议(FTP),超文本传输协议(HTTP)、gopher和文件。
*Internet服务器扩展类

2.10 其他类
*调试类
*同步类
*沙漏光标类


3. 消息处理

3.1 发送或寄送一个消息
每个窗口使用窗口进程处理发送给它的消息,而消息则来自于系统、应用程序。它告诉窗口进程执行某个任务或通知它发生了某个事件。
系统或应用程序有两种方法传输消息:发送、寄送

3.1.1 发送一个消息
发送一个消息时,直接调用窗口的窗口进程,是即时通信,直到窗口进程返回结果后应用程序才能继续执行。

3.1.2 寄送一个消息
寄送一个消息是将消息发送到拥有该窗口的应用程序的消息队列中,等应用程序有了空闲才搜索消息队列,从队列中删除消息并将其发送到指定窗口。此时,通信可能延迟,调用函数寄送消息后立即返回,不理会窗口进程的处理结果。
鼠标和键盘消息通常是寄送的,而所有其他消息通常都是发送的。通常,应该尽量发送一个消息,除非想把动作延迟到所有鼠标和键盘消息被处理之后。

3.2 使用MFC发送一个消息
首先,应获取接收消息的CWnd类对象的指针;然后,调用CWnd的成员函数SendMessage()。
        LRESULT Res=pWnd->SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam);
*pWnd指针指向目标CWnd类对象
*变量Msg是消息
*wParam和lParam变量包含消息的参数,如鼠标单击哪里或选择了什么菜单项。
*目标窗口返回的消息结果放在变量Res中。
如果要发送消息到一个没有CWnd类对象的窗口,可以用下列目标窗口的句柄直接调用Windows API:
        LRESULT Res=::SendMessage(HWND hWnd , UINT Msg , WPARAM wParam , LPARAM lParam);
*hWnd是目标窗口的句柄

3.2 使用MFC寄送一个消息
用MFC寄送一个消息与发送一个消息几乎相同,但寄送时用PostMessage( ),而不是SendMessage( );返回值Res不是一个由目标窗口返回的值,而是一个布尔值,用来表示消息是否成功地放到消息队列中。

3.2.1 检索一个寄送消息
正常情况下,一旦消息被寄送后,应用程序在后台处理它。在特殊情况下,需要你自己去删除一个消息,例如想在应用程序接收到某种消息之前停止应用程序。有两种方法可以从应用程序消息队列中删除一个消息,但这两种方法都没有涉及MFC。
第一种方法:在不干扰任何事情之下窥视消息队列,看看一个消息是否在那里。
        BOOL res=::PeekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg);
第二种方法:实际上是等待,一直等到一个新的消息到达队列为止,然后删除并返回该消息。
        BOOL res=::GetMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);

在这两种方法中,
*变量hWnd指定要截获消息的窗口,如果该变量设为NULL,所有窗口消息将被截获
*wMsgFilterMin和wMsgFilterMax变量与SendMessage( )中的变量Msg相对应,指定查看消息的范围。如果用“0,0”,则所有的消息都将被截获。如果用WM_KEYFIRST,WM_KEYLAST或WM_MOUSEFIRST,WM_MOUSELAST,则所有键盘或鼠标的消息将被截获。
*wRemoveMsg变量指定PeekMessage( )是否应该真正地从队列中删除该消息。(GetMessage总是删除消息)。该变量可以取两个值:
**PM_REMOVE,PeekMessage( )将删除消息。
**PM_NOREMOVE,PeekMessage( )将把消息留在队列里,并返回它的一个拷贝。
*lpMsg变量是一个指向MSG结构的指针,MSG包含检索到的消息。
     typedef struct tagMSG {
         HWND hwnd ; // window handle message is intended for
         UINT message;
         WPARAM wParam;
         LPARAM lParam ;
         DWORD time; // the time the message was put in the queue
         POINT pt; // the location of the mouse cursor when the message was put in the queue
     } MSG;

3.3 三类消息
*窗口消息:WM_CREATE、WM_PAINT、WM_MOUSEMOVE
*命令消息:WM_COMMAND
*控件通知:三类控件通知——WM_XXX、WM_COMMAND、WM_NOTIFY

3.4 MFC接受一个寄送的消息
消息泵

3.5 MFC怎样处理一个接收到的消息
处理接收到的消息非常简单:将消息指向一个函数,该函数通过消息中的消息标识符处理它。非MFC窗口用简单的case语句来实现该目标,每个case语句执行一些函数,或调用其他一些函数。
MFC不采用case语句,只需做下面三件事情:
*从将要接收消息的CWnd类派生类(对于命令消息是CCmdTarget)。
*在派生类中写一个处理消息的成员函数。
*在类中定义一个查找表(叫做消息映像),该表具有成员函数的条目和它要处理的消息的标识符。
然后,MFC依次调用下面的函数,指引输入消息到处理函数
1) AfxWndProc( )接收消息,寻找消息所属的CWnd对象,然后调用AfxCallWndProc( )。
2) AfxCallWndProc( )存储消息(消息标识符和参数)供未来参考,然后调用WindowProc( )。
3) WindowProc( ) 发送消息给OnWndMsg( ),然后,如果消息未被处理,则发送给DefWindowproc( )。
4) OnWndMsg( )要么为WM_COMMAND消息调用OnCommand( ),要么为WM_NOTIFY消息调用OnNotify( )。任何被遗漏的消息都将是一个窗口消息。OnWndMsg( )搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。如果OnWndMsg()不能找到这样的处理函数,则把消息返回到WindowProc( ),由它将消息发送给DefWindowProc()。
5) OnCommand( )查看这是不是一个控件通知(lParam不是NULL);如果是,OnCommand( )就试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒绝映射的消息,OnCommand( )就调用OnCmdMsg( )。
6) OnNotify( )也试图将消息映射到制造通知的控件;如果映射不成功, OnNotify( )就调用相同的OnCmdMsg( )函数。
7) 根据接收消息的类,OnCmdMsg( )将在一个称为命令传递(Command Routing)的过程中潜在地传递命令消息和控件通知。例如,如果拥有该窗口的类是一个框架类,则命令和通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值