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