3.2 MFC

MFC

graphic

Day01

MFC课程特点:

1 学习难度大

2 一个编程框架的学习。

3 基于框架,添加和修改代码。

4 代码的调试方法,断点调试。使用call stack查看

函数的调用关系。

对大家的要求:

1 不要急于求成,由理论到实践,实践理解理论的反复

的过程。

2 不要缺课,课程的连续性比较强。

3 除了掌握MFC课程的知识点之外,还需掌握代码的

调试技巧,以及阅读代码。

MFC课程的介绍

1 MFC的机制和原理 (12~15)

2 COM的应用ActiveX控件( 1 )

3 数据库ADO和网络通信Socket ( 4 )

4 项目VOD视频点播系统 (3~5)

一 MFC的概念

Microsoft Foundation Classes,微软基础类库。

包括两个方面:

1 是一个编程框架,快速开发。

2 是一个庞大的类库,可以满足我们基本的编程要求

二 MFC的历史

Borland公司-菲利普斯.康和安德鲁斯.海尔斯伯格在

1983年创建,以编译器起家的。

早在doc年代,borland c/C++,microsoft c/C++。

buck forland。

在win3.x年代,borland c/C++ 3.0/3.1.

92 visual c++ 1.0。开始挖墙脚,Borland的主要开发

人员走了(尤金.王)。

visual c++ 2.0,集成的MFC3.0主要面向32位的程序开发

visual c++4.0,集成的MFC的4.0,加入了对internet

的支持

visual c++5.0,集成了ATL(活动模版)库。

98.9 推出visual c++6.0,对类库及界面做了优化。

Microsoft与borland的编译器之之争

vs2010,集成的MFC 10.0。

三 展开的讲解MFC

1 MFC类的介绍

1.1 CObject类及它的派生类

​ 1.1.1 CObject类-MFC的顶层父类,定义了MFC的

​ 一些基本特性和机制,例如:

​ 运行时类信息、动态创建和序列化等。

​ 1.1.2 CCmdTarget类-在该类中,对命令消息做了相应的处理。如果一个类处理命令 消息,继承

​ 自CCmdTarget。

​ 1.1.3 CWinApp类-应用程序类,几乎所有的MFC

​ 程序都要使用该类,贯穿了整个程序的始终。

​ 1.1.4 CDocTemplate类,文档模板类。在该类的

​ 子类中分别定义了单文档模板和多文档模板。

​ 1.1.5 CDocument类-管理数据,包括数据的存储

​ 和加载等。

​ 1.1.6 CWnd类-提供了窗口的基本操作。类型包括

​ 框架、视图、对话框和控件。

​ 1.1.7 CFrameWnd类-框架窗口类。

​ 1.1.8 Control bars分类-控制条类,包括工具栏、

​ 状态栏等。

​ 1.1.9 CDialog类及其子类-对话框类。子类中包括

​ 一些通用对话框,字体、文件、颜色、查找替换、

​ 页面设置和打印对话框。

​ 1.1.10 Controls分类-包括了各种控件。

​ 1.1.11 CView类及其子类-视图类,显示数据。

​ 1.1.12 CException类-异常类

​ 1.1.13 CFile类-提供文件操作

​ 1.1.14 绘图相关类-包括绘图设备类和绘图对象类。

​ 1.1.15 网络编程相关类

​ ...

1.2 非CObject类的派生类

​ CArchive类-与序列化相关的类

​ CPoint、CString、CTime、CTimeSpan等

MFC与Win32的关系

MFC封装了win32的句柄和相关功能函数。

并没有完全封装Win32 API,MFC除了类之外,还有

全局函数。以"Afx"开头的函数,通常是MFC的全局函数

如果在某一作用域下,win 32函数与类的成员函数

重名,如果调用Win32函数时,加"::"。

2 MFC可以开发的程序的类型

2.1 应用程序

​ 2.1.1 单文档视图应用程序

​ 1 CWinApp-应用程序类,贯穿整个程序的

​ 始终。每个MFC的应用程序,都会有一个

​ 全局的应用程序对象theApp。

​ 2 CFrameWnd-框架窗口类。

​ 3 CEditView-视图类,显示数据,或者接受

​ 用户的输入。通常在框架窗口

​ 的客户区中显示和使用。

​ 4 CDocument-文档类,管理数据。

​ 2.1.2 多文档视图应用程序

​ 1 CWinApp

​ 2 CEditView

​ 3 CDocument

​ 4 CMDIFrameWnd-多文档主框架窗口类

​ 5 CMDIChildWnd-多文档子框架窗口类

​ 2.1.3 基于对话框的应用程序

​ 1 CWinApp

​ 2 CDialog-对话框类

2.2 库程序

​ 2.2.1 规则库(静态链接MFC dll)

​ 2.2.2 规则库(动态链接MFC dll)

​ 规则库可以被MFC和非MFC的程序使用

​ 2.2.3 MFC扩展库

​ 作用扩展MFC库的功能,只能被MFC的

​ 程序使用。

技巧:

Ctrl+Tab,在打开的两个窗口之间切换

四 编写第一个MFC程序

1 使用Win32向导生成简单的Win32应用程序

2 设置MFC环境

​ 2.1 在stdafx.h文件中,把win32的windows.h

​ 替换为MFC的afxwin.h。

​ 2.2 project->setting,选择MFCbase工程,设置

​ 使用MFC静态库。

3 编码

​ 3.1 去掉原来的WinMain()函数

​ 3.2 添加CWinApp类的派生类,使用派生类创建

​ 全局对象theApp。

​ 3.3 添加CFrameWnd类的派生类

​ 3.4 重新CWinApp::InitInstance()函数,在函数

​ 中完成窗口的创建和显示。

五 入口函数机制(MFC的第一个机制)

与Win32应用程序相同,MFC程序也是由WinMain()

函数开始,只不过WinMain()由MFC框架提供,MFC

程序不需要程序员,程序员也不能添加入口函数。

1 启动过程

1.0 创建全局对象theApp,调用父类CWinApp的构造

​ CWinApp::CWinApp()

​ {

​ //将theApp地址保存到线程状态信息中

​ pThreadState->m_pCurrentWinThread = this;

​ //确保调用AfxGetThread()函数得到theApp地址

​ ASSERT(AfxGetThread() == this);

​ //将theApp地址保存到模块状态信息中

​ pModuleState->m_pCurrentWinApp = this;

​ ASSERT(AfxGetApp() == this);

​ }

1.1 WinMain()调用了AfxWinMain()函数

1.2 在AfxWinMain()函数中,

int AfxWinMain()

{

​ /*1.2.1 获取全局对象theApp的地址,分别保存到

​ pThread和pApp两个指针变量中。*/

​ CWinThread* pThread = AfxGetThread();

​ CWinApp* pApp = AfxGetApp();

​ //1.2.2 初始化MFC库

​ AfxWinInit(...);

​ //1.2.3 应用程序的全局初始化,很少重写

​ pApp->InitApplication();

​ //1.2.4 初始化实例,通常需要在子类中重写

​ pThread->InitInstance();

​ //1.2.5 处理消息循环

​ pThread->Run();

​ //1.2.6 程序结束

​ AfxWinTerm();

​ //1.2.7 AfxWinMain()函数执行完毕

​ return nReturnCode;

}

一些快捷键

F9:设置断点

F5:调试的方式启动程序

Shift+F5:停止调试

Ctrl+F5:直接启动程序

F10:单步执行

F11:单步执行,进入被调函数

Ctrl+B:打开断点设置对话框

Ctrl+L:删除光标所在行

作业:

1 MFC概念

2 MFC常用类的功能(表格)

3 MFC应用程序的类的构成(表格)

4 编写第一个MFC程序

5 程序的执行流程(简单描述)

Day02

一 CWinApp::Run()的执行流程

int CWinThread::Run()

{

​ ASSERT_VALID(this);

​ // 是否处于空闲状态

​ BOOL bIdle = TRUE;

​ //空闲时的计数器

​ LONG lIdleCount = 0;

​ //收到WM_QUIT是退出

​ for (;;)

​ {

​ // phase1:当空闲状态为TRUE,并且没有消息时,

​ 调用OnIdle做空闲处理。游戏编程中通常重写

​ OnIdle完成场景的渲染等操作,一般程序很少

​ 重写它。

​ while (bIdle &&

​ !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))

​ {

​ // 当需要继续空闲处理时,OnIdle返回为TRUE。

​ // 当空闲处理有多个任务时,根据lIdleCount

​ // 的值设置任何的优先级。

​ if (!OnIdle(lIdleCount++))

​ bIdle = FALSE; // assume "no idle" state

​ }

​ // phase2: 循环处理消息

​ do

​ {

​ // pump message, but quit on WM_QUIT

​ if (!PumpMessage())

​ return ExitInstance();

​ // 处理完正常消息,判断是否需要空闲处理

​ if (IsIdleMessage(&m_msgCur))

​ {

​ //将空闲标志设置为TRUE

​ bIdle = TRUE;

​ lIdleCount = 0;

​ }

​ } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

​ }

​ ASSERT(FALSE); // not reachable

}

二 CFrameWnd类中对消息的处理

重写CFrameWnd::WindowProc()函数

三 窗口创建机制(MFC第二个机制)

1 回顾Win32窗口创建过程

​ 设计窗口类->注册窗口类->创建窗口->显示窗口

​ ->消息循环。

2 MFC的窗口创建过程

CFrameWnd::Create()

{

​ // 1 加载菜单

​ LoadMenu();

​ // 2 创建窗口

​ CreateEx();

​ {

​ //2.1 设置和注册窗口类

​ PreCreateWindow();

​ {

​ AfxDeferRegisterClass(8)

​ {

​ // 1 设置窗口类

​ WNDCLASS wndcls;

​ //窗口的处理函数

​ wndcls.lpfnWndProc = DefWindowProc;

​ //多种不同风格的窗口

​ if (fToRegister & 8)

​ {

​ _AfxRegisterWithIcon(...);

​ {

​ //设置窗口图标

​ LoadIcon();

​ //注册窗口类

​ AfxRegisterClass()

​ {

​ // 2 注册窗口类

​ ::RegisterClass(lpWndClass)

​ }

​ }

​ }

​ }

​ }

​ //2.2 创建和设置钩子

​ AfxHookWindowCreate(this);

​ {

​ //设置CBT类型的钩子,当窗口创建时,

​ //调用钩子函数_AfxCbtFilterHook。

​ SetWindowsHookEx(...);

​ }

​ //2.3 创建窗口,跳到钩子处理函数中

​ ::CreateWindowEx(...);

​ //2.4 卸载钩子

​ AfxUnhookWindowCreate();

​ }

}

//钩子处理函数

_AfxCbtFilterHook()

{

​ //1 将窗口句柄附加到窗口对象

​ Attach()

​ {

​ SetPermanent(...)

​ {

​ //以窗口句柄为键,对象地址为值建立映射关系

​ m_permanentMap[(LPVOID)h] = permOb;

​ }

​ }

​ //2 将原来注册的窗口处理函数DefWindowProc替换

​ 为MFC提供的标准的窗口处理函数AfxWndProc

​ //获取MFC标准的窗口处理函数

​ WNDPROC afxWndProc = AfxGetAfxWndProc();

​ //设置到窗口

​ oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,

​ (DWORD)afxWndProc);

}

四 CFrameWnd类中的消息处理过程

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

{

​ // 1 根据窗口句柄获取窗口对象地址

​ CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

​ // 2 调用AfxCallWndProc,注意参数包括了pWnd

​ return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);

​ {

​ //根据窗口对象指针调用成员函数WindowProc,

​ //根据虚函数机制,调用CMainFrame::WindowProc

​ pWnd->WindowProc(nMsg, wParam, lParam);

​ {

​ switch (message)

​ {

​ case WM_PAINT:

​ {

​ ....

​ }

​ return CFrameWnd::WindowProc(message,wParam,lParam);

​ {

​ LRESULT lResult = 0;

​ if (!OnWndMsg(message, wParam, lParam, &lResult))

​ //CMainFrame以及MFC的类中没有处理的消息,

​ //交给DefWindowProc做默认处理。

​ lResult = DefWindowProc(message, wParam, lParam);

​ return lResult;

​ }

​ }

​ }

}

总结:消息的入口函数是MFC所提供的AfxWndProc,

根据虚函数的机制,让程序员优先处理关注的消息,

程序员不关注的消息由DefWindowProc做默认处理。

---------------------------------------------

MFC通过虚函数的机制,让框架的流程能够调用我们

所写的代码,实现我们想要的功能。

1 应用程序类CWinApp可以重写的虚函数

1.1 InitInstance()函数,创建所需各种对象

1.2 Run()函数,自己定义消息循环

1.3 OnIdle()函数,处理空闲时的任务

1.4 ExitInstance()函数,对象、资源等清理工作

2 窗口类CFrameWnd可以重写的虚函数

2.1 WindowProc()函数,处理消息

2.2 PreCreateWindow()函数,可以设置窗口的风格

​ 和窗口的大小、位置等

3 成员变量

CWinApp m_pMainWnd

CMainFrame m_hWnd

作业:

1 问题:窗口注册时,处理函数是DefWindowProc,

但是,实际处理时,使用的CMainFrame::WindowProc,

为什么?

2 问题:CMainFrame类的对象与它表示的窗口应该有

一一对应的关系,这种关系什么时候建立的?

Day03

一 消息映射

消息映射的目的,把庞大的switch-case结构拆分,

一个函数对应一个消息处理。

1 消息映射的实现

1.1 在类的定义中添加消息映射的声明宏

​ DECLARE_MESSAGE_MAP()

1.2 在类的实现中添加消息映射的实现宏,

​ 并且添加消息ID与消息函数的映射

​ BEGIN_MESSAGE_MAP(CMainFrame,CFrameWnd)

​ ON_MESSAGE(WM_CREATE,OnCreate)

​ ON_MESSAGE(WM_PAINT,OnPaint)

​ END_MESSAGE_MAP()

1.3 添加消息处理函数的声明和实现

2 消息映射的实现原理

2.1 展开宏

2.2 成员的类型

2.2.1 //这个结构类似于链表的节点

​ struct AFX_MSGMAP

​ {

​ // 1 指向基类的AFX_MSGMAP变量的指针

​ const AFX_MSGMAP* pBaseMap;

​ // 2 指向类中结构体数组的指针

​ const AFX_MSGMAP_ENTRY* lpEntries;

​ };

2.2.2 存储消息的信息

​ struct AFX_MSGMAP_ENTRY

​ {

​ UINT nMessage; // 消息的ID,例如:WM_CREATE

​ UINT nCode; // 消息的通知码,例如:EN_CHANGE

​ UINT nID; // 控件的ID

​ UINT nLastID; // 在一个范围中,最大的控件ID

​ UINT nSig; // 消息处理函数的类型

​ AFX_PMSG pfn; // 消息处理函数的函数指针

​ };

​ 2.3 成员的作用

​ 2.3.1 _messageEntries[]-静态的结构体数组,保存了当前

​ 类处理的消息的信息。

​ 2.3.2 messageMap-静态的结构体变量。保存了父类的

​ messageMap变量的地址和当前的_messageEntries[]

​ 数组的地址

​ 2.3.3 GetMessageMap()-虚函数,返回了当前类的

​ messageMap变量的地址。

​ 2.4 成员之间的关系

​ GetMessageMap()

​ |->&messageMap

​ |-> 消息ID与消息处理函数的地址...

​ |->&CFrameWnd::messageMap

​ |->消息ID与消息处理函数的地址...

​ |->&CWnd::messageMap

​ |->消息ID与消息处理函数的地址...

​ |->&CCmdTarget::messageMap

​ |->消息ID与消息处理函数的地址...

​ |->NULL

​ 最终形成了一个继承层次关系上的消息的信息链表

​ 2.5 执行流程

​ WindowProc没有重写,调用CWnd::WindowProc()

​ LRESULT CWnd::WindowProc(...)

​ {

​ LRESULT lResult = 0;

​ if (!OnWndMsg(message, wParam, lParam, &lResult))

​ //当整个循环查找中没有被处理的消息,由

​ //DefWindowProc默认处理

​ lResult = DefWindowProc(message, wParam, lParam);

​ return lResult;

​ }

​ BOOL CWnd::OnWndMsg(...)

​ {

​ pMessageMap = GetMessageMap();

​ for (; pMessageMap != NULL;

​ pMessageMap = pMessageMap->pBaseMap)

​ {

​ //如果找到,返回结构体数组中元素的地址

​ if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,

​ message, 0, 0)) != NULL)

​ {

​ goto LDispatch;

​ }

​ }

​ //找到消息对应的结构体后,跳到LDispatch

​ LDispatch:

​ //保存各种类型的消息处理函数的地址

​ union MessageMapFunctions mmf;

​ //将消息的处理函数的指针赋值给联合体保存

​ mmf.pfn = lpEntry->pfn;

​ //将消息处理函数的类型保存到变量nSig中

​ nSig = lpEntry->nSig;

​ switch(nSig)

​ {

​ ...

​ case AfxSig_lwl:

​ //pfn_lwl 保存的是CMainFrame::OnCreate地址

​ //根据函数指针,调用OnCreate()函数

​ lResult = (this->*mmf.pfn_lwl)(wParam, lParam);

​ break;

​ ...

​ }

3 MFC根据对消息处理方式的不同,划分为以下几种

类型:

3.1 窗口消息(标准消息)

以WM开头,除WM_COMMAND之外的所有消息,这类消息

循环查找过程是在CWnd::OnWndMsg()函数中完成。

消息映射宏:

ON_WM_XXX()

3.2 命令消息

3.2.1 命令消息

来自于菜单、工具栏、加速键等消息。

消息映射宏:

ON_COMMAND()

ON_COMMAND_RANGE()

注意:当多个控件有相同的处理流程时,才会让

一个处理函数对应多个控件。

3.2.2 命令消息的执行流程

CCmdTarget::OnCmdMsg()

{

​ for (pMessageMap = GetMessageMap();

​ pMessageMap != NULL;

​ pMessageMap = pMessageMap->pBaseMap)

​ {

​ //根据消息的ID查找结构体元素

​ lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);

​ if (lpEntry != NULL)

​ {

​ //根据函数指针lpEntry->pfn调用函数OnTest()

​ return _AfxDispatchCmdMsg(this, nID, nCode,

​ lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);

​ }

​ }

}

3.3 通知消息

大部分的控件的消息都是通知消息。例外,按钮的

单击消息按命令消息来处理的。

3.4 自定义消息

3.4.1 定义消息的标识

​ #define WM_USERMSG (WM_USER+100)

3.4.2 添加消息映射宏

​ ON_MESSAGE(WM_USERMSG,fn)

3.4.3 添加消息处理函数的声明和实现

​ afx_msg LRESULT fn(WPARAM wParam,LPARAM lParam);

3.4.4 发送消息,可以在程序员需要的任何流程中

​ 发送消息

​ SendMessage();

-------------------------------------------

总结:

1 窗口消息的循环查找在CWnd::OnWndMsg()完成。

2 命令消息的循环查找在CCmdTarget::OnCmdMsg()完成

如果希望自定义的类能够处理菜单、工具栏等命令

消息,一,该类派生自CCmdTarget类,二,该类有

消息映射。

mazhiguo01@163.com

Day04

一 MFC菜单

1 回顾win32中,如何为窗口添加菜单的?

​ 1.1 设计窗口类时,可以设置菜单

​ 1.2 在创建窗口的函数中传参

​ 1.3 使用Win32函数动态创建菜单

2 CMenu类

​ 将菜单句柄与操作菜单的Win3 API进行封装。

2.1 CMenu的相关操作

​ 2.1.1 加载菜单

​ CMenu::LoadMenu

​ 2.1.2 对象与菜单句柄的附加和分离

​ CMenu::Attach/Detach

​ 2.1.3 显示右键菜单

​ CMenu::TrackPopupMenu

​ ...

二 MFC工具栏

1 相关类

CToolbar类-封装了工具栏与框架窗口之间的关系。

CToolBarCtrl类-封装了工具栏包含的控件以及工

​ 具栏本身的操作。

头文件:#include

2 CToolbar的使用

​ 2.1 创建工具栏窗口

​ CToolbar::Create/CreateEx

​ 2.2 加载工具栏

​ CToolbar::LoadToolBar

​ 2.3 停靠工具栏

​ CToolbar::EnableDocking

​ CFrameWnd::EnableDocking

​ CFrameWnd::DockControlBar

​ 2.4 显示/隐藏工具栏

​ CFrameWnd::ShowControlBar

​ 2.5 设置标题

​ CWnd::SetWindowText

​ 2.6 设置提示信息

​ 2.6.1 设置风格包括CBRS_TOOLTIPS

​ 2.6.2 为工具栏的每一个按钮添加提示信息

三 MFC状态栏

1 相关类

CStatusbar类-封装了状态栏与框架窗口的关系

CStatusbarCtrl类-封装了状态内部的操作。

2 CStatusbar类的使用

Day05

一 MFC视图

1 视图相关类

CView类-抽象基类,所有视图类的父类。

2 CView的使用

2.1 添加CView的派生类,并且实现纯虚函数OnDraw。

​ OnDraw()是视图类中最重要的函数,作用是显示数据。

2.2 处理窗口销毁问题 (ViewCore.cpp)

2.2.1 介绍 CWnd::PostNcDestroy

​ 窗口销毁时,最后一个被调用的函数。是一个

​ 虚函数,子类重写实现对象的自我销毁。这样

​ 的子类对象通常new的方式生成。

2.3 视图窗口大小随着框架客户区大小的改变而改变,

​ 两者大小一致。

2.4 将视图设置为活动视图

​ 2.4.1 通过函数设置的方式

​ SetActiveView(m_pWndView);

​ 2.4.2 通过指针赋值的方式

​ m_pViewActive=m_pWndView;

3 对命令消息的响应顺序

​ CView->CFrameWnd->CWinApp

在CFrameWnd::OnCmdMsg()函数中确定了上面的顺序

二 运行时类信息(MFC的第四个机制)

1 定义

程序在运行时,获得对象的类的信息以及类的继承

层次关系。

class CDog:CAnimal:CObject

CDog dog;

1> 获取类的名称"CDog",大小sizeof(dog),版本等 信息

2> 继承层次关系

dog is a CDog? TRUE

dog is a CAnimal? TRUE

dog is a CObject? TRUE

dog is a CWnd? FALSE

2 相关函数

​ 2.1 CObject::GetRuntimeClass()

​ 获取类的信息

​ 2.2 CObject::IsKindOf

​ 判断对象是否属于某个类

3 实现

3.1 派生自CObject类

3.2 在类中添加运行时类信息的声明宏

​ DECLARE_DYNAMIC(CAnimal)

3.3 在类外添加运行时类信息的实现宏

​ IMPLEMENT_DYNAMIC(CDog,CAnimal)

4 实现原理

4.1 展开宏

4.2 相关结构体

struct CRuntimeClass

{

​ LPCSTR m_lpszClassName;//类名

​ int m_nObjectSize;//大小

​ UINT m_wSchema; //版本

​ //创建对象的函数指针,在运行时类信息中,为NULL

​ CObject (PASCAL m_pfnCreateObject)();

​ //指向父类的运行时类信息变量地址

CRuntimeClass* m_pBaseClass;

...

}

4.3 成员的作用

classCDog-静态的结构体成员变量,类型是

​ CRuntimeClass。保存了当前类的信息,

​ 例如:类名、大小和版本等。

GetRuntimeClass-虚函数,作用获取classCDog

​ 的地址

4.4 成员之间的关系

GetRuntimeClass()

​ |->&classCDog

​ |->类名、大小、版本等信息

​ |->&classCAnimal

​ |->类名、大小、版本等信息

​ |->&classCObject

​ |->类名、大小、版本等信息

​ |->NULL

最终形成了一个继承层次关系的类的信息链表。

分析函数的执行过程: CDog classCDog

dog.IsKindOf(RUNTIME_CLASS(CWnd))

{

//获取CDog::classCDog变量的地址

CRuntimeClass* pClassThis = GetRuntimeClass();

//将当前对象的运行时类信息的地址与参数中

类的运行时类信息的地址进行循环比较;如果相等,则表示对象属于该类;否则对象不属于该类。

return pClassThis->IsDerivedFrom(pClass);

{

​ const CRuntimeClass* pClassThis = this;

​ while (pClassThis != NULL)

​ {

​ if (pClassThis == pBaseClass)

​ return TRUE;

​ pClassThis = pClassThis->m_pBaseClass;

​ }

​ return FALSE;

}

}

三 动态创建(MFC的第五个机制)

1 定义

创建未知类型的对象

2 实现

2.1 派生自CObject类

2.2 在类中添加动态创建的声明宏

​ DECLARE_DYNCREATE

2.3 在类外添加动态创建的实现宏

​ IMPLEMENT_DYNCREATE

3 实现原理

3.1 展开宏

3.2 成员的作用

​ 在运行时类信息的基础上多了一个函数

​ 3.2.1 CreateObject()-这个函数的作用创建

​ 当前类的对象。

​ 3.2.2 classCAnimal-类型是CRuntimeClass,这个结构体变量保存了CAnimal::CreateObject()

​ 函数的地址。

3.3 创建过程

​ Create(RUNTIME_CLASS(CAnimal));

​ {

​ //调用了CRuntimeClass的函数[classCAnimal]

​ CObject *pObj=pClass->CreateObject();

​ {

​ /*m_pfnCreateObject保存了

​ CAnimal::CreateObject()的地址 */

​ pObject = (*m_pfnCreateObject)();

​ {

​ }

​ }

​ }

Day06

一 MFC的切分窗口

1 切分窗口的类型

1.1 静态切分

​ 在程序编写时就已经确定的窗口切分。使用的

​ 视图类可以不相同。

1.2 动态切分

​ 程序在运行时,由用户实时的完成窗口切分。

​ 使用的是同一个视图类。

2 相关类

CSplitterWnd类-作用就是完成窗口切分。

头文件:#include

3 相关函数

CFrameWnd::OnCreateClient -作用是创建窗口的客户

区对象。是一个虚函数。会被消息处理函数OnCreate

调用,我们不会显示的调用它。

4 静态切分的实现

​ 4.1 创建静态切分

​ CSplitterWnd::CreateStatic

​ 4.2 创建视图

​ CSplitterWnd::CreateView

5 静态切分窗口的再切分

​ 注意:再切分时,父窗口是上一个切分对象,

​ 切分的ID不使用默认值,来自于函数。

​ 5.1 CSplitterWnd::IdFromRowCol()

​ 5.2 创建视图

​ CSplitterWnd::CreateView

​ 5.3 设置分隔条的位置

​ CSplitterWnd::SetColumnInfo

​ CSplitterWnd::SetRowInfo

6 动态切分的实现

6.1 指定切分窗口使用的视图类

​ CCreateContext cxt;

​ cxt.m_pNewViewClass=RUNTIME_CLASS(CMyView);

6.2 创建动态切分窗口

​ CSplitterWnd::Create

问题:1 CMyView可以处理菜单消息吗?

​ 2 CEditView中输入字母为什么会出错?

​ 3 切分后的视图窗口是独立的吗?

二 MFC文档类

1 相关类

CDocument类-作用是用来管理数据。

2 文档类的使用

2.1 视图显示文档中的数据

2.1.1 CView::OnInitialUpdate-虚函数,视图的

​ 初始化更新函数,在第一次附加文档之后,

​ 更新显示之前被调用。

2.1.2 CView::GetDocument()

2.1.3 CFrameWnd::InitialUpdateFrame-框架的

​ 初始化更新函数,引起2.1.1函数的调用。

3 创建过程

3.1 CFrameWnd::OnCreate()函数

​ 在函数中,层层调用,调用CreateView()函数

​ 动态创建视图对象,并且创建视图窗口

​ CreateView()

​ {

​ // 1 动态创建视图对象

​ pContext->m_pNewViewClass->CreateObject();

​ // 2 创建视图窗口

​ pView->Create(..);

​ }

3.2 CView::OnCreate()函数

​ 在函数中,调用AddView()函数

​ pContext->m_pCurrentDoc->AddView(this);

​ {

​ //在文档中,使用链表保存视图对象地址

​ m_viewList.AddTail(pView);

​ /*在视图中,使用成员m_pDocument保存

​ 文档对象的地址*/

​ pView->m_pDocument = this;

​ }

​ 一个文档的数据可以被多个视图显示。一个

​ 视图只能显示一个文档的数据。

明天在当文档视图架构程序中,框架、视图和文档

都要采用动态创建的方式。目的1,创建方式更加

统一;目的2,框架可以更好封装三者之间关系的

建立过程。

作业:1 文档类添加后,可以处理菜单消息的,处理

​ 的优先级是什么?

Day07

一 MFC的单文档视图程序

1 相关类

1.1 CWinApp-应用程序类

1.2 CFrameWnd-框架窗口类

1.3 CView-视图类

1.4 CDocument-文档类

1.5 CDocTemplate-抽象基类,定义了文档模板的

​ 所有功能。子类包括单文档模板类和多文档

​ 模板类。

​ CSingleDocTemplate-单文档模板类

​ CSingleDocTemplate(

​ UINT nIDResource, //资源ID

​ CRuntimeClass* pDocClass, //文档类的运行时类信息

​ CRuntimeClass* pFrameClass, //框架类的运行时类信息

​ CRuntimeClass* pViewClass//视图类的运行时类信息

​ );

2 对命令消息的默认处理顺序

​ View->Document->Frame->App

3 创建过程

3.1 创建单文档模板对象

3.2 将文档模板添加到应用程序

AddDocTemplate(pTemplate);

{

​ if (m_pDocManager == NULL)

​ m_pDocManager = new CDocManager;

​ //使用文档管理类的对象管理文档模板

​ m_pDocManager->AddDocTemplate(pTemplate);

​ {

​ //使用文档模板链表保存文档模板的指针

​ m_templateList.AddTail(pTemplate);

​ }

}

3.3 新建文档

OnFileNew();

{

​ //调用文档管理类的OnFileNew()

​ m_pDocManager->OnFileNew();

​ {

​ //从文档模板链表中得到文档模板

​ CDocTemplate* pTemplate =

​ (CDocTemplate*)m_templateList.GetHead();

​ //调用文档模板函数创建文档

​ pTemplate->OpenDocumentFile(NULL);

​ {

​ // 1 动态创建文档对象

​ pDocument = CreateNewDocument();

​ {

​ m_pDocClass->CreateObject();

​ }

​ // 2 创建框架对象和窗口

​ pFrame = CreateNewFrame(pDocument, NULL);

​ {

​ //2.1 创建框架对象

​ m_pFrameClass->CreateObject();

​ //2.2 创建框架窗口

​ pFrame->LoadFrame(...);

​ /*

​ 2.3 在CFrameWnd::OnCreate()函数

​ 中,创建视图对象和窗口。

​ 2.4 在CView::OnCreate()函数中,

​ 视图与文档相互保存对方地址。

​ */

​ }

​ }

​ }

}

3.4 显示和更新窗口

4 类与类(对象与对象)之间的关系

CWinApp

​ |->m_pMainWnd (CFrameWnd)

​ |->m_pActiveView(CView)

​ |->m_pDocument(CDocument)

​ |->m_viewList (CView List)

​ |->m_pDocManager(CDocManager)

​ |->m_templateList (CSingleDocTemplate List)

​ |->m_onlyDoc (CDocument)

4.1 获取theApp对象

​ AfxGetApp( );

4.2 获取CFrameWnd对象

​ AfxGetMainWnd()

​ theApp.m_pMainWnd

4.3 获取CView对象

​ AfxGetMainWnd()->GetActiveView()

4.4 获取CDocument对象

​ AfxGetMainWnd()->GetActiveView()->GetDocument()

5 使用MFC向导生成单文档应用程序

5.1 CWinApp-应用程序类

​ InitInstance()-还可以在该函数中完成各种库

​ 的初始化。

​ ExitInstance()-库的卸载以及资源等清理工作。

​ Run()-重新定义消息循环

​ OnIdle()-空闲处理

5.2 CFrameWnd-框架窗口类

​ PreCreateWindow()-修改窗口的风格

​ OnCreate()-添加工具栏、修改状态栏、以及自

​ 定义的一些框架窗口的子窗口等等。

5.3 CView-视图类

​ OnDraw()-完成各种数据的显示以及数据的输入等

​ OnInitialUpdate()-视图的初始化更新

5.4 CDocument-文档类

​ Serialize()-序列化函数,保存和加载数据。

二 MFC的多文档应用程序

1 相关类

1.1 CWinApp

1.2 CMDIFrameWnd

1.3 CMDIChildWnd

1.4 CView以及CEditView

1.5 CDocument

1.6 CMultiDocTemplate

与单文档的区别:

1 多了子框架窗口

2 子框架窗口与主框架窗口分别拥有自己的图标

​ 和菜单。

3 注意:主框架窗口菜单的菜单项至少两项

2 编写多文档应用程序

2.1 “新建”菜单,子框架、视图和文档对象都被创建

2.2 "新建视图",基于原来的活动视图对应的文档,

​ 创建子框架和视图。不创建新的文档。最终

​ 多个视图对应同一个文档。

Day08

一 MFC绘图

1 相关类

1.1 绘图设备类

1.1.1 CDC类-绘图设备类的父类。表示的是一般的

绘图设备,例如:打印机、显示器等设备。

1.1.2 CWindowDC类-父类是CDC类,表示的是指定

的窗口,包括非客户区和客户区。

1.1.3 CClientDC类-父类都是CDC类,表示的是窗口

的客户区。

1.1.4 CPaintDC类-父类也是CDC类,表示的是窗口

的客户区。该类只能在WM_PAINT消息的处理函数中

使用。

1.1.5 CMetaFileDC类-父类也是CDC类,与其它DC的

区别最大,它保存了图形绘制的命令。在需要重新

绘制该图形时,从该类的对象中获取命令,重新

绘制。

1.2 绘图对象类

1.2.1 CPen类-画笔

1.2.2 CBrush类-画刷

1.2.3 CFont类-字体

1.2.4 CBitmap类-位图

1.2.5 CPalette类-调色板

1.2.6 CRgn类-区域

调色板的引入可以降低位图占用的空间大小,本质

上是一个颜色表。

RGB(0~255,0~255,0~255),3个字节,24位真彩色

800600像素的位图:800600*3 字节

使用256调色板的位图:8006001+颜色表

2 使用

2.1 绘图设备类的使用

2.1.1 CDC类的使用

​ 1 创建DC

​ CDC::CreateDC

​ CreateDC(

​ LPCTSTR lpszDriverName,//设备的驱动名称

​ LPCTSTR lpszDeviceName,//设备名称

​ LPCTSTR lpszOutput, //设备接口,设置NULL

​ const void* lpInitData //设备的初始化参数

​ );

​ 如果设备是显示器时,

​ CreateDC("DISPLAY",NULL,NULL,NULL);

​ 2 使用

​ 3 删除DC

​ CDC::DeleteDC

2.1.2 CDC子类的使用

​ 直接构造对象使用,不需要创建和销毁

2.1.3 CMetaFileDC的使用步骤

​ 1 创建

​ CMetaFileDC::Create

​ 2 绘制

​ ...

​ 3 关闭,返回DC的句柄 HMETAFILE

​ CMetaFileDC::Close

​ 4 重新绘制(多次使用)

​ CDC::PlayMetaFile(HMETAFILE)

​ 5 删除

​ DeleteMetaFile(HMETAFILE)

​ 2.2 绘图对象的使用

​ 2.2.1 画笔

​ 1 构造或者创建画笔

​ 2 将画笔选入绘图设备

​ 3 绘制

​ 4 将画笔从绘图设备中选出

​ 5 删除画笔

​ 2.2.2 画刷、字体步骤与画笔相同

​ 2.2.3 位图

​ 位图是通过一个兼容dc的拷贝绘制

​ 2.2.4 区域

​ 1 创建区域

​ CRgn::CreateXXX

​ 2 将两个区域几何运算,运算可以多次

​ CRgn:CombineRgn

​ 3 填充

​ CDC::FillRgn

​ 4 填充边框

​ CDC::FrameRgn

二 简单的鼠标绘图的例子

图形包括直线、矩形和椭圆三种。

在视图类中添加消息:

1 LBUTTONDOWN消息

2 MOUSEMOVE消息

3 LBUTTONUP消息

Day09

一 MFC文件操作

1 相关类

1.1 CFile类-封装了文件句柄以及操作文件的API。

​ 提供了文件内容读写的功能,还提供

​ 了文件属性的设置和获取。

1.2 CFileFind类-提供了文件查找的功能。

2 CFile类的使用

2.1 文件的读写

​ 1 打开或新建文件

​ CFile::Open

​ 2 文件读写

​ CFile::Read/Write

​ 3 设置文件指针位置

​ CFile::SeekToBeigin

​ CFile::SeekToEnd

​ CFile::Seek

​ 4 关闭文件

​ CFile::Close

​ 注意:1 文件读写通常放到try—catch块中

​ 2 文件读写时,要有相应的权限

​ 3 文件读写时,注意文件指针位置

​ 2.2 获取/设置文件属性

​ CFile::GetStatus

​ CFile::SetStatus

3 CFileFind类的使用

​ 3.1 开始查找

​ CFileFind::FindFile

​ 3.2 查找下一个文件[得到第一个文件的信息,

​ 返回下一个文件是否存在]

​ CFileFind::FindNextFile

​ 3.3 获取/判断文件信息

​ CFileFind::GetXXX/IsXXX

​ 3.4 结束查找

​ CFileFind.Close

练习1:查找某一指定目录下的文件和目录

练习2:查找某一指定目录下的所有的文件和目录

​ 1 递归

​ 2 排除.目录

二 CArchive类-归档类,引入它代替具体的文件读写,

好处:1 可以设置缓冲区大小 2 读写时,不需要

​ 类型转换。

1 序列化

​ 以二进制流的方式将数据依次写入到文件或者

​ 依次从文件中读取数据的过程,称为序列化。

2 CArchive的使用步骤:

​ 1 打开或新建文件

​ CFile::Open

​ 2 文件读写

​ 2.1 定义CArchive对象,[可以指定缓冲区大小]

​ 2.2 具体的读写操作

​ CArchive::operator << 写操作

​ CArchive::operator >> 读操作

​ 2.3 关闭CArchive对象

​ CArchive::Close

​ 3 关闭文件

​ CFile::Close

三 MFC的对象序列化(MFC的第六个机制)

1 概念

序列化对象-以二进制流的方式将对象的类信息以

及对象的成员变量依次写入到文件的过程

反序列化对象-以二进制流的方式将类的信息从文件

中读取并创建对象,然后读取成员变量的值赋值

给新建的对象的过程。

2 定义支持序列化的类

2.1 派生自CObject类

2.2 在类的定义中添加序列化的声明宏

​ DECLARE_SERIAL(classname)

2.3 在类的实现中添加序列化的实现宏

​ IMPLEMENT_SERIAL(classname,basename,wchema)

2.4 重写CObject::Serialize()函数,在函数中,完成

​ 成员变量的序列化。

3 对象序列化的使用

对象的序列化可以像一般类型变量一样操作方便。

需要注意的是,运算符函数>>、<<的参数是对象

的指针类型。

练习:定义一个支持序列化的类CCourse

​ 课程编号ID(int)

​ 课程名称CourseName (CString)

​ 教师姓名 teacherName (CString)

​ //授课教师 teacher (CTeacher)

3 实现原理

3.1 展开宏

​ struct AFX_CLASSINIT

​ { AFX_CLASSINIT(CRuntimeClass* pNewClass)

​ {

​ AfxClassInit(pNewClass);

​ {

​ //将当前类的运行时类信息classCStudent的地址

​ //保存到应用程序的m_classList链表中。

​ pModuleState->m_classList.AddHead(pNewClass);

​ }

​ }

​ };

3.2 成员的作用

​ operator>>-当前类的友元函数,作用读取对象。

​ _init_CStudent-类型是AFX_CLASSINIT,全局变量,

​ 在进入main()函数前,该变量被构造,调用

​ AfxClassInit()函数,classCStudent的地址保存

​ 到m_classList链表中。

3.3 写对象的过程

​ ar.WriteObject(pOb);

​ {

​ // 1 获取当前类的运行时类信息

​ CRuntimeClass* pClassRef = pOb->GetRuntimeClass();

​ // 2 将类的信息写入到文件

​ WriteClass(pClassRef);

​ {

​ //依次将类的版本、类名称长度以及类名称写入

​ //到文件

​ pClassRef->Store(*this);

​ {

​ WORD nLen = (WORD)lstrlenA(m_lpszClassName);

​ ar << (WORD)m_wSchema << nLen;

​ ar.Write(m_lpszClassName, nLen*sizeof(char));

​ }

​ }

​ // 3 将成员变量的值写入到文件

​ ((CObject)pOb)->Serialize(this);

​ {

​ if (ar.IsStoring())

​ {

​ ar<<m_strName<<m_iAge;

​ }

​ ...

​ }

​ }

​ 3.4 读取对象的过程

​ ar.ReadObject(RUNTIME_CLASS(CStudent));

​ {

​ ReadClass(...);

​ {

​ // 依次从文件中读取类的版本、类名称

​ //长度以及类的名称,根据类的名称在

​ //m_classList查找,返回运行时类信息

​ CRuntimeClass::Load(...)

​ {

​ ar >> wTemp;

​ ar >> nLen;

​ ar.Read(szClassName,...);

​ for (pClass = pModuleState->m_classList; pClass != NULL;

​ pClass = pClass->m_pNextClass)

​ {

​ if (lstrcmpA(szClassName,...)

​ {

​ return pClass;

​ }

​ }

​ }

​ }

​ //使用运行时类信息创建对象

​ pOb = pClassRef->CreateObject();

​ // 从文件中读取数据初始化新建的对象

​ pOb->Serialize(*this);

​ {

​ ...

​ else

​ {

​ ar>>m_strName>>m_iAge;

​ }

​ }

​ }

Day10

一 MFC对话框

1 相关类

1.1 CDialog类-所有对话框类的父类。

1.2 通用对话框类-包括以下6种:

​ 1.2.1 CFileDialog-文件对话框

​ 1.2.2 CColorDialog-颜色对话框

​ 1.2.3 CFontDialog-字体对话框

​ 1.2.4 CFindReplaceDialog-查找替换对话框

​ 1.2.5 CPrintDialog-打印对话框

​ 1.2.6 CPageSetupDialog-页面设置对话框

1.3 CPropertyPage类-属性页对话框

2 在Win32向导使用MFC类创建对话框

2.1 基于模式对话框的应用程序

​ 2.1.1 插入对话框资源,并且与对话框类关联

​ 2.1.2 创建和显示对话框

​ CDialog::DoModal()

​ 2.1.3 关闭对话框

​ CDialog::OnOK/OnCancel

2.2 基于非模式对话框的应用程序

​ 2.2.1 创建与一般的框架窗口的创建过程相似

​ 2.2.2 对话框的关闭需要程序员处理

​ 1 重写CDialog::OnOK和OnCancel()函数

​ 在函数中销毁对话框

​ DestroyWindow()

​ 2 重写CWnd::PostNcDestroy()函数,

​ 在函数中删除对话框本身

​ delete this;

2.3 DoModal()函数的执行过程

​ 2.3.1 查找和加载对话框资源

​ 2.3.2 将父窗口设置为不可用状态

​ 2.3.3 根据资源,创建对话框窗口

​ 2.3.4 进入对话框的消息循环

​ 2.3.5 在将父窗口设置为可用之前,隐藏对话框

​ 2.3.6 将父窗口的状态设置为可用的和活动的

​ 2.3.7 销毁对话框窗口

​ 2.3.8 释放对话框资源

​ 2.3.9 返回DoModal函数的结果

二 控件操作

对话框的数据交换技术

引入它的目的是更加方便的操作控件,将控件与

对话框类的成员变量绑定,然后通过访问和操作

成员变量的方式操作控件。

1 对话框数据交换的使用

1.1 在对话框中添加成员变量

​ CWnd m_wndOK;

1.2 在对话框中添加并实现DoDataExchange()函数

​ CWnd::DoDataExchange

1.3 在DoDataExchange()函数中,完成变量与控件

​ 的绑定

​ DDX_Control() -控件类型的绑定

​ DDX_Text()-值类型的绑定

1.4 UpdateData(BOOL)-完成变量与控件的数据交换

​ UpdateData(TRUE)-将用户在控件中输入的数据

​ 传递给变量。

​ UpdateData(FALSE)-将变量的值显示到控件上

2 使用DDX完成一个简单的登录操作

​ user:csd1402 pwd:456

​ 例子,加法运算

三 MFC的控件的介绍

1 静态控件-包括图片、文本和分组框控件,ID值

​ 默认都是IDC_STATIC。通常很少操作静态控件,

​ 主要是为用户显示相关信息。如果需要绑定成

​ 员绑定并在代码中操作控件,需要重新设置每个

​ 控件的ID值,保证相互不重复。

2 按钮控件-包括一般按钮、单选按钮和复选按钮。

​ 它们所使用的控件类都是CButton。如果在一个

​ 对话框中有多个单选分组的话,注意设置单选

​ 按钮的Group属性。

3 组合框-组合框是在多个选项中选择一项,也可以

​ 编辑选项。可以通过属性对话框添加该控件的选

​ 项。

Day11

一 MFC控件

1 列表框-在多个选项中可以选择一项,也可以选择

​ 多项,不接收用户输入。只能通过函数添加列表框

​ 的数据项。

1.1 CListBox类的使用

​ 1.1.1 添加数据项

​ CListBox::AddString

​ 1.1.2 删除数据项

​ CListBox::DeleteString

​ 1.1.3 清空列表控件中所有数据项

​ CListBox::ResetContent

​ 1.1.4 获取/设置当前选择项

​ CListBox::GetCurSel/SetCurSel

​ 1.1.5 获取数据项的文本

​ CListBox::GetText

​ ...

2 CAnimateCtrl类-动画类,播放简单动画。

​ 2.1 打开动画文件(*.avi)

​ CAnimateCtrl::Open

​ 2.2 播放动画

​ CAnimateCtrl::Play

​ 2.3 停止播放

​ CAnimateCtrl::Stop

3 例子-播放简单动画

3.1 CFileDialog类的介绍

3.1.1 CFileDialog(

​ BOOL bOpenFileDialog,//表示对话框是打开还是另存为

​ LPCTSTR lpszDefExt = NULL, 默认的扩展名称

​ LPCTSTR lpszFileName = NULL,默认的文件名称

​ DWORD dwFlags = OFN_HIDEREADONLY,对话框的风格

​ LPCTSTR lpszFilter = NULL, 设置文件类型的字符串

​ CWnd* pParentWnd = NULL//父窗口,通常设置为NULL

​ );

​ 表示文件类型的字符串格式:

​ 1 每种类型之间用|隔开,整个字符串以"||"结束

​ 2 每种类型分两部分,显示部分和后缀部分,这

​ 两部分以"|"分隔。

​ "视频文件(.avi)|.avi|所有文件(.)|.||";

​ 3.1.2 获取打开的文件路径

​ CFileDialog::GetPathName()

​ 3.1.3 获取文件名称

​ CFileDialog::GetFileName()

​ ...

3.2 CListBox类的相关函数

​ 3.2.1 查找字符串

​ CListBox::FindString

​ 3.2.2 设置/获取附加数据

​ CListBox::SetItemData

​ CListBox::GetItemData

3.3 CButton类的相关函数

​ CButton::GetCheck

​ ....

4 旋转按钮、进度条和滑块的使用

4.1 相关类

CSpinButtonCtrl-旋转按钮类

CProgressCtrl-进度条类

CSliderCtrl-滑块类

4.2 使用

4.2.1 设置控件表示的数值范围

​ SetRange/GetRange

​ SetRange32/GetRange32

4.2.2 设置控件的增量或步长

​ CSpinButtonCtrl::SetAccel

​ CProgressCtrl::SetStep

​ CSliderCtrl::SetLineSize

​ CSliderCtrl::SetPageSize

4.2.3 设置/获取控件的当前位置

​ SetPos/GetPos

5 列表控件

5.1 相关类

CListCtrl-列表控件,控件类型,常用在对话框中

CListView-列表视图,视图类型,常用在框架窗口中

在视图中操作时,通常调用CListView::GetListCtrl

获取相关的控件,通过调用控件的函数完成功能。

5.2 CListCtrl类的使用

5.2.1 CListCtrl的样式

Day12

一列表控件

1 相关类

CListCtrl-列表控件,控件类型,常用在对话框中

CListView-列表视图,视图类型,常用在框架窗口中

在视图中操作时,通常调用CListView::GetListCtrl

获取相关的控件,通过调用控件的函数完成功能。

2 CListCtrl类的使用

2.1 CListCtrl的样式

​ 图标、小图标、列表和报表

2.2 图标列表类

​ CImageList::Create()

​ BOOL Create(

​ UINT nBitmapID, 位图资源ID

​ int cx,//在位图中,每个图标的宽度

​ int nGrow,//集合的自动增长量

​ COLORREF crMask//遮挡色

​ );

2.3 设置控件的图标列表

​ CListCtrl::SetImageList

2.4 设置控件的列

​ CListCtrl::InsertColumn

2.5 插入数据项

​ CListCtrl::InsertItem

2.6 设置列的文本

​ CListCtrl::SetItemText

2.7 删除所有数据项

​ CListCtrl::DeleteAllItems

2.8 设置/获取数据项的附加数据

​ CListCtrl::SetItemData/GetItemData

2.9 查找符合条件的数据项

​ CListCtrl::FindItem

2.10 设置控件的背景图片

​ 1 需要Ole库的初始化

​ AfxOleInit()

​ 2 设置背景图片

​ CListCtrl::SetBkImage

​ 3 设置文本的背景颜色透明

​ CListCtrl::SetTextBkColor(CLR_NONE);

...

二 MFC的树控件

1 相关类

CTreeCtrl-控件类,通常用在对话框上。

CTreeView-视图类,通常用在框架窗口中。

2 CTreeCtrl的使用

树控件是由一个个的节点组成。节点之间的关系包

括父子关系和兄弟关系,通过节点句柄标识节点。

2.1 设置树控件的图标列表

CTreeCtrl::SetImageList

2.2 插入节点

CTreeCtrl::InsertItem

HTREEITEM InsertItem(

LPCTSTR lpszItem,//节点文本

int nImage, //节点的图标索引

int nSelectedImage, //选中时的节点的图标索引

HTREEITEM hParent = TVI_ROOT, //节点的父节点

HTREEITEM hInsertAfter = TVI_LAST);

2.3 设置节点高度

CTreeCtrl::SetItemHeight

2.4 设置节点的展开状态

CTreeCtrl::Expand

...

三 属性页对话框

1 分类

标签式属性页-常用于参数、选项等配置

向导式属性页-常用于引导用户通过一步步的设置

​ 和选择完成某种功能。

2 相关类

CPropertyPage-页面类,父类是CDialog。

CPropertySheet-表单类,父类是CWnd。

一个属性页=一个表单对象+多个页面对象。

3 标签式属性页的使用步骤:

3.1 添加页面对话框资源,修改语言和字体字号等。

3.2 双击资源窗口生成新的对话框类,注意父类

​ 一定是CPropertyPage类。

3.3 添加表单类,派生自CPropertySheet类。

3.4 在表单类中添加页面的对象,并且在表单类的

​ 构造函数中,通过调用AddPage()函数,添加

​ 页面对象。

3.5 创建和显示标签式属性页

​ CPropertySheet::DoModal()

Day13

一 对话框中添加加速键的例子

1 插入加速键资源,并且添加加速键与控件ID的关系

2 在对话框的初始化函数中,加载加速键资源

​ ::LoadAccelerators

3 重写对话框的PreTranslateMessage函数

​ ::TranslateAccelerator

二 属性页对话框

1 分类

标签式属性页-常用于参数、选项等配置

向导式属性页-常用于引导用户通过一步步的设置

​ 和选择完成某种功能。

2 相关类

CPropertyPage-页面类,父类是CDialog。

CPropertySheet-表单类,父类是CWnd。

一个属性页=一个表单对象+多个页面对象。

3 标签式属性页的使用步骤:

3.1 添加页面对话框资源,修改语言和字体字号等。

3.2 双击资源窗口生成新的对话框类,注意父类

​ 一定是CPropertyPage类。

3.3 添加表单类,派生自CPropertySheet类。

3.4 在表单类中添加页面的对象,并且在表单类的

​ 构造函数中,通过调用AddPage()函数,添加

​ 页面对象。

3.5 创建和显示标签式属性页

​ CPropertySheet::DoModal()

3.6 将应用按钮设置为可用

​ CPropertyPage::SetModified

3.7 向导按钮的消息处理通过重写虚函数的方式添加

​ CPropertyPage::OnApply

​ CPropertyPage::OnCancel

​ CPropertyPage::OnOK

4 向导式属性页的使用步骤:

4.1 添加页面对话框资源,修改语言和字体字号等。

4.2 双击资源窗口生成新的对话框类,注意父类

​ 一定是CPropertyPage类。

4.3 添加表单类,派生自CPropertySheet类。

4.4 在表单类中添加页面的对象,并且在表单类的

​ 构造函数中,通过调用AddPage()函数,添加

​ 页面对象。

4.5 将属性页设置为向导式

​ CPropertySheet::SetWizardMode

4.6 创建和显示

​ CPropertySheet::DoModal

4.7 设置每个页面的向导按钮

​ 4.7.1 在OnSetActive()函数中设置(虚函数)

​ 4.7.2 首先在页面中获取表单对象

​ CPropertyPage::GetParent

​ 4.7.3 设置向导按钮

​ CPropertySheet::SetWizardButtons

4.8 消息处理

​ CPropertyPage::OnSetActive

​ CPropertyPage::OnCancel

​ CPropertyPage::OnWizardNext

​ CPropertyPage::OnWizardFinish

​ CPropertyPage::OnWiardBack

三 MFC的线程

1 根据线程的用途将线程分为两类:

1.1 工作者线程-常用于后台比较耗时的操作。

1.2 用户界面线程-在线程中,可以拥有自己的用户

​ 界面,可以与用户交互。

2 使用

2.1 工作者线程的使用步骤:

2.1.1 定义线程函数

​ UINT MyControllingFunction( LPVOID pParam );

2.1.2 创建和启动线程

​ AfxBeginThread

2.2 用户界面线程的使用步骤:

2.2.1 定义CWinThread的派生类

2.2.2 在类的InitInstance()函数中创建界面

2.2.3 创建和启动线程

​ AfxBeginThread

3 线程同步

​ MFC提供了四个线程同步类。

四 完成一个综合的例子

Day14

一 TabControl控件的使用

1 CTabCtrl::InsertItem

​ 插入新的标签

2 创建每个标签对应的对话框,注意对话框的属性

​ style:child;border:none

3 创建对话框窗口,父窗口是Tab控件

4 设置对话框大小以及显示状态

5 在Tab控件的TCN_SELCHANGE消息处理函数中,

​ 设置对话框的显示状态。

二 ActiveX控件

1 COM的概念

​ Component Object Model,组件对象模型。

​ 是一些小的可以执行的二进制文件,通过接口对

​ 外为其它的程序、项目或组件提供服务。

​ 一个*.exe程序。

​ 一个项目=一个或多个.exe+多个.dll。

2 接口的概念

​ C语言-接口就是函数

​ C++语言-接口是公有的成员函数。

​ COM组件-接口就是一组纯虚函数的集合。

​ virtual void Sort(char [] arr)=0;

​ 组件内部实现了该接口

3 ActiveX控件

​ 基于COM组件技术的,可以像一般控件一样方便

​ 使用。与一般控件不同的是,任何支持微软的

​ COM标准的计算机语言都是使用该组件,甚至可以

​ 直接在html页面中使用。

4 使用向导开发ActiveX控件

​ 4.1 工程中的类和接口的介绍

​ 4.1.1 第一个接口-用来添加控件的属性和方法的

​ 4.1.2 第二个接口-用来添加事件的

​ 4.1.3 App-应用程序类

​ 4.1.4 Ctrl-控件类,实现接口的属性、方法和事件

​ 在该类的OnDraw函数中,设计控件的外观

​ 4.1.5 PropPage-属性 页类。

​ 4.2 在OnDraw()函数中绘制控件外观

​ 4.3 通过第一个接口添加控件的方法和属性

​ 4.3.1 添加方法

​ void SetNumber(int a,int b);

​ 实现时的调用关系:

​ SetNumber->Invalidate->OnDraw->Mcd

​ 4.3.2 添加属性

​ 4.4 通过第二个接口添加控件的事件

​ 4.4.1 库存事件

​ 4.4.2 用户自定义事件

​ 需要用户输入事件名称,并且在相应

​ 的代码中调用Fire事件名()触发自定义

​ 事件。

5 控件的注册

5.1 在使用MFC向导开发组件时,编辑生成后,

​ 开发环境自动完成控件的注册。

5.2 微软提供了一个工具专门用于控件注册

​ regsvr32.exe。使用方法:

​ 在运行框中,输入regsvr32 ocx文件路径 注册组件。

​ regsvr32 /u ocx文件路径,卸载组件。

6 控件的使用

6.1 在MFC的窗口程序中使用

​ 在使用控件时,都要首先确保控件的封装类能

​ 够添加到该工程。然后,可以像使用一般控件

​ 一样使用该控件。

6.2 在Html页面中使用

7 常用的ocx控件

7.1 Microsoft Communication Control

​ 串口通信控件

7.2 Microsoft DataGrid Control6.0

​ 用与显示数据库表的表格控件

7.3 Windows Media Player控件

Day15

一 Windows平台下访问数据库技术

1 ODBC-Open Database Connectivity,微软开放式

​ 数据互联。可以使用一组相同的API访问和操作不

​ 同的数据库。只能访问关系型数据库。MFC将这组

​ API函数进行封装,提供了ODBC类。

2 DAO-基于ODBC的,被淘汰。

3 OLE DB-基于COM组件技术的,提供了一组用于访问

​ 数据库的接口。既可以访问关系型又可以访问非

​ 关系型数据库。

4 ADO-基于OLE DB的,对OLE DB的接口进行了封装,

​ 提供了一组简单的接口。逐渐流行起来。

5 ADO.NET-基于NET平台的,一组用于访问数据库的

​ 类。可以被VB.NET、C#、C++等多种语言使用。

二 ODBC类的使用

1 相关类

CDatabase类-提供数据库的连接和关闭功能,另外提供

​ 了执行Sql语句的功能。

CRecordset类-提供了对数据的操作。

#include

2 设置ODBC数据源

设置->控制面板->ODBC数据源

3 ODBC类的使用步骤:

3.1 打开数据源

​ CDatabase::Open

3.2 执行Sql语句

​ CDatabase::ExecuteSQL

3.3 打开表

​ CRecordset::Open

3.4 字段的信息

​ CRecordset::GetODBCFieldInfo

​ CRecordset::GetODBCFieldCount

3.5 获取字段的值

​ CRecordset::GetFieldValue

3.6 游标操作

​ CRecordset::Move

​ CRecordset::MoveFirst

​ CRecordset::MoveNext

​ CRecordset::MoveLast

​ CRecordset::MovePrevious

​ CRecordset::IsEOF

​ CRecordset::IsBOF

3.7 关闭记录集

​ CRecordset::Close

3.8 关闭数据库

​ CDatabase::Close

三 ADO组件的使用

1 ADO组件的文件路径:msado15.dll

​ "C:\Program Files\Common Files\System\ado\"

2 #import语句导入ADO组件

#import "ado的文件路径" no_namespace rename("EOF","adoEOF")

编译后,自动生成msado15.tlh和msado15.tli文件。

3 初始化/卸载COM库。

4 组件的接口更多的是COM数据类型,在MFC程序中

​ 直接使用接口时,需要c++与COM之间的类型转换。

​ 在实际开发中,将ADO组件的接口封装成c++的类,

​ 接口函数封装成类的成员函数。

5 ADO组件的常用接口

​ Connection接口-功能类似于CDatabase类。

​ Recordset接口-功能类似于CRecordset类。

5.1 使用CAdoDatabase类封装Connection接口

​ 5.1.1 连接数据库

​ HRESULT Open (

​ _bstr_t ConnectionString,//连接字符串

​ _bstr_t UserID,//登录名称

​ _bstr_t Password,//密码

​ long Options/* 数据库打开方式。

​ 如果不指定,直接写-1 */

​ );

​ 使用*.UDL文件自动生成连接字符串.

​ 5.1.2 关闭数据库

​ Close();

5.2 使用CAdoRecordset类封装Recordset接口

HRESULT Open (

​ const _variant_t & Source,//表名、Sql语句、存储过程名

​ const _variant_t & ActiveConnection,//活动连接

​ enum CursorTypeEnum CursorType,//游标类型

​ enum LockTypeEnum LockType

​ ,//记录的锁定类型

​ long Options//选项,标识第一个参数到底是什么?

​ adCmdTable adCmdText adCmdStoredProc

​ );

​ enum CursorTypeEnum

​ {

​ adOpenForwardOnly = 0,//单向的静态游标

​ adOpenKeyset = 1,//键集游标,动态游标的一种

​ adOpenDynamic = 2,//动态游标

​ adOpenStatic = 3//静态游标

​ };

​ 静态游标看不到其它用户对数据库的修改;动态

​ 游标是可以看到其它用户对数据的修改。

​ enum LockTypeEnum

{

​ adLockReadOnly = 1,//只读的

​ adLockPessimistic = 2,//悲观锁

​ adLockOptimistic = 3,//乐观锁

​ adLockBatchOptimistic = 4//批处理的乐观锁

};

​ ShowData(CAdoRecordset* pSet)

Day16

一 Recordset接口

1 字段的操作 (Fields)

1.1 获取字段数量

​ Fields->GetCount()

1.2 获取字段的名称

​ Fields->GetItem(nIndex)->GetName()

1.3 或者/设置字段的值

​ Fields->GetItem(nIndex)->Value

1.4 游标操作

​ MoveFirst()

​ MoveLast()

​ MoveNext()

​ MovePrevious()

​ Move(int nNum)

​ BOOL IsBOF()

​ BOOL IsEOF()

1.5 增、删、改的 操作

​ 1.5.1 添加数据

​ 1 添加新记录

​ AddNew

​ 2 设置记录中每个字段的值

​ 3 更新到数据库

​ Update

​ 1.5.2 删除数据

​ 1 将游标移动到要删除的记录

​ MoveLast()等

​ 2 执行删除操作

​ Delete()

​ 3 更新到数据库

​ Update()

​ 1.5.3 修改数据

​ 1 将游标移动到要修改的记录

​ MoveLast()等

​ 2 执行修改操作(重新设置字段的值)

​ SetFieldValue()

​ 3 更新到数据库

​ Update()

二 执行Sql语句

1 Connnection接口中的Execute()函数

​ Execute (

​ _bstr_t CommandText, //Sql语句

​ VARIANT * RecordsAffected, //执行Sql语句,影响的记录的数量

​ long Options // adCmdText

​ )

​ 如果Sql语句是查询语句,该函数返回的记录集

​ 是单向的静态游标,记录集是只读的。

2 Recordset接口中的Open()函数

​ 通过该函数得到的记录集是可以通过参数设置

​ 游标类型和记录的锁定类型的。

三 事务处理

1 ADO组件在Connection接口中也提供的事务处理。

BeginTrans()-开始事务

CommitTrans()-提交事务

RollBackTrans()-回滚事务

四 项目要求管理图片或者视频数据?

在实际项目开发中,通常存储图片或者视频文件的

路径。

五 windows平台下的网络通信编程(Socket)

1 socket函数-通用的网络编程接口。

2 以WSA开头的socket函数-基于windows平台,结合

​ 了windows平台特性的API函数。

3 MFC 部分的封装了相关函数,提供了socket类。

​ CAsyncSocket-异步socket类,父类是CObject类。

​ CSocket类-同步socket类,父类是CAsyncSocket类

​ 只适合开发小型的网络通信编程。

4 socket库的版本

4.1 MFC6.0封装了1.1版本的socket库

4.2 xp系统提供了2.2版本的socket库

5 socket库的相关文件

​ 1 dll文件:ws2_32.dll

​ 2 lib文件:ws2_32.lib

​ 3 头文件:winsock.h

6 socket库的初始化和卸载

​ 1 int WSAStartup(

​ WORD wVersionRequested, //请求使用的库的版本

​ LPWSADATA lpWSAData//返回可用的库的信息

​ );

​ 2 int WSACleanup()

7 TCP通信和UDP通信

7.1 面向连接的TCP通信

​ 7.1.1 TCP服务器

​ 1 创建socket

​ 2 绑定IP和端口

​ 3 监听

​ 4 接收客户端连接

​ 5 数据收发

​ 6 关闭socket

​ 7.1.2 TCP客户端

​ 1 创建socket

​ 2 连接服务器

​ 3 数据收发

​ 4 关闭socket

练习1:编写客户端代码与服务器通信

练习2: 试试不同平台的socket通信(IP和Port相同)

netstat -an,查看网络端口状态的

7.2 面向无连接的UDP通信

作业:编写UDP的简单通信

Day17

一 使用MFC的类完成文件传输

二 FeiQ例子

1 功能:用户上线

​ 信息收发

​ 文件传输

​ 用户下线

​ 掉线检测

2 工程中类和结构体的说明

2.1 _tag开头的一组结构体,作用封装客户端与

​ 服务器之间传输的数据的。

2.2 CUserServer类-UDP服务器

2.3 CUserClient类-UDP客户端

​ 用户的上线、下线、信息收发和掉线检测都

​ 使用了UDP通信。

2.4 CFileServer类-TCP服务器

​ CFileClient类-TCP客户端

​ 文件传输使用的是TCP通信

2.5 CFileRecvDlg类-文件发送对话框

​ CFileSendDlg类-文件接收对话框

2.6 CFileThread类-进行文件收发时,用户界面线程

​ 的线程类。

2.7 CUserView类-用户列表视图

2.8 CChatView类-用户聊天视图

​ ...

3 流程分析

3.1 用户上线的执行流程(聊天和下线类似)

3.1.1 m_UserServer.InitServer()

​ 创建UDP服务器端socket,绑定地址和端口。

​ 创建和启动子线程,在线程函数中,接收

​ 并处理客户端数据 ...

3.1.2 m_UserClient.InitClient()

​ 创建UDP客户端socket,通过调用setsockopt

​ 设置socket为广播模式。

3.1.3 m_UserClient.Broadcast( );

​ 使用结构体变量封装数据,发送到服务器。

3.1.4 服务器收到数据后,调用OnUserBroadcast()

​ 处理,在函数中,通过分别调用两个视图的

​ AddView()函数,将信息分别添加到列表控件

​ 和组合框控件中。

3.2 用户下线的执行流程

​ ...

3.3 掉线检测

​ 通过发送心跳数据。

​ 3.3.1 在CMainFrame的OnCreate()函数中,创建

​ 和启动定时器,每隔10秒,发送一次广播消息。

​ 服务器收到后,将用户列表控件每个数据项的

​ 附加数据设置为1。

​ 3.3.2 在CUserView的OnCreate()函数中,创建

​ 和启动定时器,每隔20秒,调用一次处理函数。

​ 在函数中,判断附加数据值,如果为1,设置为0;

​ 如果为0,则删除该项。

​ 正常情况:1 0 1 0 1 0 1 0 1 0 1...

​ 掉线情况:1 0 1 0 1 0 1 0 0,删除该项

// 窗口搭建

// wincreate.cpp : Defines the entry point for the application.

#include "stdafx.h"

HINSTANCE g_hInstance = 0; //接收当前程序实例句柄

//窗口处理函数

LRESULT CALLBACK WndProc( HWND hWnd,UINT nMsg,WPARAM wParam,LPARAM lParam)

{

​ switch ( nMsg )

​ {

​ case WM_DESTROY:

​ PostQuitMessage( 0 );//能够使getMessage返回0

​ break;

​ }

​ return DefWindowProc( hWnd, nMsg, wParam,lParam );

}

//注册窗口类

void Register(LPSTR lpClassName,WNDPROC wndproc )

{

​ WNDCLASSEX wce = { 0 };

​ wce.cbSize = sizeof( wce ); //结构体的大小

​ wce.cbClsExtra = 0;//窗口类缓冲区

​ wce.cbWndExtra = 0;//窗口缓冲区

​ wce.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);//背景色(灰)

​ wce.hCursor = LoadCursor(NULL,IDC_ARROW);//光标

​ wce.hIcon = NULL;//窗口大图标句柄

​ wce.hIconSm = NULL;//窗口小图标句柄

​ wce.hInstance = g_hInstance;//当前模块句柄

​ wce.lpfnWndProc = wndproc;//窗口处理函数

​ wce.lpszClassName = lpClassName;//窗口类名称

​ wce.lpszMenuName = NULL;//菜单名

​ wce.style = CS_VREDRAW | CS_VREDRAW;//窗口风格

​ RegisterClassEx( &wce );//注册,向系统写入

}

//创建主窗口

HWND CreateMain( LPSTR lpClassName, LPSTR lpWndName )

{

HWND hWnd = CreateWindowEx( 0,

lpClassName,

lpWndName,

WS_OVERLAPPEDWINDOW,

​ CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

CW_USEDEFAULT,

NULL,

ULL,

g_hInstance,

​ NULL );

​ return hWnd;

}

//显示(绘制)窗口

void Display(HWND hWnd )

{

​ ShowWindow( hWnd,SW_SHOW );

​ UpdateWindow( hWnd);

}

//消息循环

void Message()

{

​ MSG uMsg = { 0 };

​ while( GetMessage(&uMsg,NULL,0,0)) //获取消息

​ {

​ TranslateMessage( &uMsg ); // 翻译,分析

​ DispatchMessage( &uMsg ); // 调度,派发

​ }

}

int APIENTRY WinMain(HINSTANCE hInstance,

​ HINSTANCE hPrevInstance,

​ LPSTR lpCmdLine,

​ int nCmdShow)

{

​ g_hInstance = hInstance;

​ Register("Main",WndProc);

​ HWND hWnd = CreateMain( "Main","Windows");

​ Display( hWnd);

​ Message();

​ return 0;

}


打赏

微信支付 支付宝支付

License

本作品由Simonhttp://www.uusystem.com)创作,采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 欢迎转载,但任何转载必须保留完整文章,在显要地方显示此声明以及原文链接。如您有任何疑问或者授权方面的协商,请邮件:postmaster@uusystem.com。

results matching ""

    No results matching ""