MFC 是一个编程框架
转自:http://wenku.baidu.com/view/743fbcddad51f01dc281f15b.html
MFC 中的各种类结合起来构成了一个应用程序框架,它的目的就是让程序员在此基础上来
建立Windows 下的应用程序。MFC 框架定义了应用程序的轮廓,并提供了用户接口的标准
实现方法。AppWizard 可以用来生成初步的框架文件。资源编辑器用于帮助直观的设计用户
接口。ClassWizard 用来协助添加代码到框架文件,最后,通过类库实现了应用程序特定的
逻辑。
MFC 提供了一个Windows 应用程序开发模式,对程序的控制主要是由MFC 框架完成的。
而且MFC 也完成了大部分的功能,预定义或实现了许多事件和消息处理。框架或者由其本
身处理事件,不依赖程序员的代码,或者调用程序员的代码来处理应用程序特定的事件。
1.SDI 生成
1.步骤dxq2009
首先,打开VC++6.0 开发环境,然后,选择”File”菜单中的“New”子菜单,在弹出的对话
框中选择“MFC AppWizard(exe)”项并在“Progect name”编辑框中输入合适的工程名字
Simple1,如图,它的意思是创建一个基于MFC 的应用,接着进入正式的创建过程,MFC
应用程序的创建过程有6 步(基于对话框)或者6 步(SDI 或者MDI),下面首先介绍SDI
应用的创建过程。
(1) 第一步用于选择应用的结构以及语言等。如图1,首先确定应用是否需要Doc/View
Architecture Support 支持,因为不使用该结构的应用不支持从磁盘文件打开文档,
也没有派生于类CWnd 的窗口客户区。上面3 个单选按钮用于确定创建的应用类型,
包括单文档,多文档,对话框,这里选择第一个。然后从资源列表框选择应用所使
用的语言种类,单击“Next”。
图 1
(2)第二步为用用程序选择4 项数据库支持选项之一:如图2.如果选择了数据库支持,那
么单击“Data Source”按钮,选择外部的数据库表项,一般按默认即可,单击“Next”。
图 2
(3)第三步选择希望包含在应用中的复合文档支持项,同时判定是否启用标准的ActiveX
资源,以及是否为应用的菜单条添加额外的自动化命令等,如图4,一般安默认,单击“Next”
图4
(4)第四步用于选择应用所需的基本用户接口特征,以及所想使用的工具栏类型,如图5,
如果想要修改应用所使用的文件名和扩展名,或者想要调整应用的用户接口和框架风格,就
单击“Advanced”,然后修改,一般默认,单击“Next”。
图 5
(5)第五步设置工程的风格,Explorer 风格的应用类似于资源管理器,标准MFC 风格带有
文件视图区域,还要判定是否希望应用向导在源文件中生成注释,最后选择MFC 库时动态
链接还是静态链接,如图6 单击“Next”。
图6
(6)第六步可以更改由应用向导提供的默认类型,基类,头文件和实现文件名,对于视图,
还可以更改它的基类,如图7,一般默认,单击”Finish”,在弹出的工程信息对话框中点击
“OK”即结束应用的创建过程。
图 7
2.MFC 工程的成员类及全局对象
应用向导可以自动地生成 MFC 应用的各个C++类,另外,还能自动的生成一个类APP 的全
局对象theApp,如图8 下面做简要说明。
图8
1.应用类及全局对象(CCExcmpleApp)
应用类封装了 Windows 应用的初始化,运行以及终止的全过程。对于每一个基于框架的应
用,它必须有一个且只能有一个派生于CWinApp 的类对象。这个对象是全局对象,因此它
在创建任何窗口前首先被构造。类CWinApp 提供了几个关键的可重载的虚成员函数,他们
是InitInstance,Run,ExitInstance 以及OnIdle 等。而且,在程序中可以随时调用全局函数
AfxGetApp,以便获得CWinApp 类对象的指针。
2.文档类(CCExcmpleDoc)
文档类实际上是一种数据结构,该类实现了对这种结构的封装以利于管理,通常,它不但包
含应用中所需的数据,而且也包含了处理这些数据的方法,另外,文档类还可以为应用提供
与其存储的数据相关的服务。
3.视图类(CCExcmpleView)
该类占有框架窗口的客户区,主要负责显示文档数据,也为文档对象和用户之间提供了用以
交互的可视接口,另外,也完成了与文档打印相关的操作,通常,一般的绘制操作都是在该
类中完成,因此有时也称视图类窗口为“绘制窗口”。
4.框架类(CMainFrame)
框架类表示应用程序的主框架窗口,其主要作用是响应标准的窗口消息,不过,它通常先将
消息按照一定的次序传递给视图类以及文档类等其他命令处理类,另外,它还为视图类提供
可视化的边框,同时也包括标题栏,一些标准的窗口组件等。
5.“关于”对话框类(CAboutDlg)
该类封装了用于显示软件版本,版权等相关信息的“关于”对话框,通常不需要对它进行任
何的编程。而只需要使用对话框资源编辑器对对话框模板进行简单的编辑即可。
3.源文件结构:
应用向导生成的应用程序具有很多原始的功能,例如:打开文件对话框等,而且还可以使用
类向导向某个类添加成员函数或者成员变量,而且类向导可以将添加的成员安排在何时得位
置。应用向导和类向导时怎么样实现这些自动功能呢?下面先浏览一下CCExcmpleView 的
头文件:
// CExcmpleView.h : interface of the CCExcmpleView class
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_CEXCMPLEVIEW_H__4FEB3544_9956_4E4C_93C9_35D40796D187__INCLUDED_)
#define AFX_CEXCMPLEVIEW_H__4FEB3544_9956_4E4C_93C9_35D40796D187__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CCExcmpleView : public CView
{
//Constructors
protected: // create from serialization only
CCExcmpleView();
DECLARE_DYNCREATE(CCExcmpleView)
// Attributes
public:
CCExcmpleDoc* GetDocument();
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCExcmpleView)
public:
virtual void OnDraw(CDC* pDC); // overridden to draw this view
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
该代码的作用是声明CCExcmpleView 类,但是这个声明包含在一个“#iif…#define…#endif”
结构内,其目的时保证编译时此文件只被包含一次。下面介绍下头文件的组成部分。主要由
注释块,访问类型以及分界符。
1.注释块:用“//”引导的绿色部分
Constructors 块:构造块,用于声明该类的C++构造函数,以及所需的各种初始化函数。
Attributes 块 : 共性或属性快,用于包含对象的共性或属性,
Operations 块:操作块,用于包含成员函数,可以通过对象调用这些函数,以使该对象执行
需要的任务或操作,
Overridables 块:重载块,该块用于包含虚函数,当需要更改基类的行为时,可以在派生类
中重载这些函数。
Implementation 块;实现块,是MFC 类声明中最重要的部分。实现块包括所有的实现信息,
包括成员变量和成员函数。
2.访问类型。
Public ,protected,private
3.分界符:
virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CCExcmpleView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CCExcmpleView)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG // debug version in CExcmpleView.cpp
inline CCExcmpleDoc* CCExcmpleView::GetDocument()
{ return (CCExcmpleDoc*)m_pDocument; }
#endif
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif
// !defined(AFX_CEXCMPLEVIEW_H__4FEB3544_9956_4E4C_93C9_35D40796D187__INCLUDED_)
从上面的代码可以看到如“//{{AFX_MSG(CCExcmpleView)”等的标识符,类向导使用几种
特殊的分界符,用以区分向导生成的代码和用户输入的代码,这些格式化的分界符以注释的
形式出现在代码中。如下所示:
分界符 简要描述
通用定界符
AFX_MSG 在头文件中标志与消息映射相关的ClassWizard 实体
AFX_MSG_MAP 在实现文件的类的消息映射中标志消息映射的起止
AFX_VIRTUAL 在头文件中标志虚函数重载声明的起止
对话框定界符
AFX_DATA 在头文件中标志用于对话框数据交换(DDX)的成员变量声明的起止
AFX_DATA_INIT 在对话框类的构造函数中标志DDX 的成员变量初始化的起止
AFX_DATA_MAP 在对话框类的DoDataExchange 函数中标志DDX 函数调用的起止
记录集定界符
AFX_FIELD 在头文件中标志用于数据库记录字段交换的成员变量声明的起止
AFX_FIELD_INIT 在记录集类的构造函数中标志RFX 的成员变量初始化的起止
AFX_FIELD_MAP 在记录集类的DoFieldExchange 函数中标志RFX 函数调用的起止
OLE 定界符
AFX_DISP 在头文件中标志OLE 自动化声明的起止
AFX_DISP_MAP 在实现文件中标志OLE 自动化映射的起止
AFX_EVENT 在头文件中标志OLE 事件声明的起止
AFX_EVENT_MAP 在实现文件中标志OLE 事件的起止
“DECLARE_DYNCREATE(CCExcmpleView)”是MFC 为支持该类的动态创建而提供的宏。
4.应用程序类:MFC 程序的启动过程:终止过程
1.全局对象的产生:
全局对象在名为 Global 的文件夹中,此时只有一个theApp,从C++的学习中可以了解到,
当操作系统将程序加载并激活时,全局对象将首先获得配置,因此其构造函数将首先被执行,
也即时说它比WinMain 更早,下来看这个构造函数到底做了什么。
CCExcmpleApp theApp;
从程序中看到,它自己的构造函数只是完成用户自定义的变量的初始化,而运行环境的初始
化是在它的基类中完成的,它的基类时CWinApp,它的构造函数定义如下:
CWinApp::CWinApp(LPCTSTR lpszAppName)
{//参数时Windows 使用的应用名称
if (lpszAppName != NULL)
m_pszAppName = _tcsdup(lpszAppName);
else
m_pszAppName = NULL;
// initialize CWinThread state 初始化CWinThread state 状态
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this;
ASSERT(AfxGetThread() == this);
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
// initialize CWinApp state 初始化CWinApp 状态
ASSERT(afxCurrentWinApp == NULL);
// only one CWinApp object please 存储theApp 对象的指针
pModuleState->m_pCurrentWinApp = this;
ASSERT(AfxGetApp() == this);
// in non-running state until WinMain 直到运行WinMain 时,才是真正意义上的运行状态
m_hInstance = NULL;
m_pszHelpFilePath = NULL;// 应用程序帮助文件的路径
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_pRecentFileList = NULL;
m_pDocManager = NULL;
m_atomApp = m_atomSystemTopic = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
// initialize wait cursor state
m_nWaitCursorCount = 0;
m_hcurWaitCursorRestore = NULL;
// initialize current printer state
m_hDevMode = NULL;
m_hDevNames = NULL;
m_nNumPreviewPages = 0; // not specified (defaults to 1)
// initialize DAO state
m_lpfnDaoTerm = NULL; // will be set if AfxDaoInit called
// other initialization
m_bHelpMode = FALSE;
m_nSafetyPoolSize = 512; // default size
}
CWinApp 的基类时CWinThread,CWinThread 表示具有一个或多个线程的应用程序的主执
行线程。
从上面代码可以看到,此函数主要时用来对线程和全局对象的初始化,同时保存 theApp 对
象的指针,这样WinMain 可以通过该指针调用它的成员函数,用以初始化和执行该应用.
2.应用程序入口-WinMain 函数以及主框架创建
全局对象生成后,系统根据配置的 CRT DLL(C-Runtime DLL,C 运行时动态链接库)对
WinMain 函数进行调用,这些工作是由系统自动完成的,WinMain 函数的定义如下:
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
_tWinMain 中的_t 是为了支持Unicode 而定义的映射宏。
从代码里面看到,WinMain 只是对另外一个函数AfxWinMain 的简单调用,下面看
AfxWinMain 函数的实现过程。如下:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization AFX 内部初始化 dxq
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare) APP 全局初始化 dxq
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run(); //dxq 进入Run 状态
InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm(); //dxq 终止应用运行并注销环境
return nReturnCode;
}
从代码看出,该函数主要调用了 4 个关键的函数:AfxWinInit(),InitApplication(),
InitInstance(),Run(),
在此就不详细的介绍这几个函数,有兴趣的学生可以自己区看看。
AfxWinInit():-初始化MFC 环境,
主要完成两个任务:其 1:初始化全局对象的数据成员,其2,初始化线程指定的数据。
InitApplication():内部管理
InitInstance():应用的初始化
讲解:每当应用程序启动一个新的实例时,WinMain 就调用InitInstance,从概念上讲,应用
的初始化过程可分为两个部分,程序第一次运行时,将进行应用级的初始化,当程序的一个
副本或“实例”运行时,它将进行实例的初始化。此函数时虚函数,而且在CWinApp 中是
一个空函数,因此,在派生类中必须对此函数进行重载,应用向导会完成这个任务。下面是
它的函数体:
BOOL CCExcmpleApp::InitInstance()
{
。。。。。。。
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CCExcmpleDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CCExcmpleView));
AddDocTemplate(pDocTemplate);
//dxq 解析命令行为标准的外壳命令
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 发送指定的命令
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 惟一的窗口已经被初始化,显示并更新
m_pMainWnd->ShowWindow(SW_SHOW);
//更新窗口发送WM_PAINT 消息
m_pMainWnd->UpdateWindow();
return TRUE;
}
主框架创建
此函数完成了 MFC 程序的大部分任务,如命令解析,框架,视图乃至文档的生成等,但是,
这些过程对开发人员来说时不透明的,他们被MFC 封装起来。
在应用程序开始执行时,由 APP 类辅助解析命令行,执行下面两行代码,接着执行
ProcessShellCommand 函数,,在这个函数里面调用了一系列封装的函数,主要完成创建一个
主框架窗口,学员可以进入函数了解一下。再次略去。至此,主框架就创建好了,主框架的
显示以及更新也在InitInstance 函数中。在主框架产生之际会发出WM_CREATE 消息,因此
CMainFrame::OnCreate 会被执行,哪里将进行工具栏和状态栏的建立,及如下代码:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE |
CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// TODO: Delete these three lines if you don't want the toolbar to
// be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
工具栏和状态栏分别由CToolBar 和CStatusBar 创建,两个对象属于主窗口。为了拦截
WM_CREATE,首先需要在MessageMap 中设定“映射项目”;
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code !
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
ON_WM_CREATE 这个宏表示,只要WM_CREATE 发生,OnCreate 就会调用,就会创建工
具栏和状态栏。
至此,程序就已经启动起来了,启动完毕后,程序进入挂起状态。
3.对象的创建
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CCExcmpleDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CCExcmpleView));
AddDocTemplate(pDocTemplate);
//dxq 解析命令行为标准的外壳命令
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// 发送指定的命令
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// 惟一的窗口已经被初始化,显示并更新
m_pMainWnd->ShowWindow(SW_SHOW);
//更新窗口发送WM_PAINT 消息
m_pMainWnd->UpdateWindow();
此段代码显示了对象的创建
SDI 应用通常包括几个重要的对象:文档模板,文档,框架窗口以及视图等。应用对象负责
创建文档模板,而文档模板负责创建文档和框架窗口,框架窗口负责创建视图对象,其先后
顺序为:文档模板->文档->框架窗口->视图。
对象创建完成之后,
4.程序的挂起:
Run():程序挂起。
Run()函数通过消息循环,检查消息队列中是否有需要处理的消息,如果有消息需要处理,
则Run()就获取-翻译—分发它,如果没有任何消息需要处理,则Run 调用OnIdle 以便执
行用户或框架需要完成的空闲时间处理如果没有任何消息,也没有任何可执行的空闲处理,
则应用程序一直等待消息产生,应用也就被挂起。
下面时函数定义:
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting
application.\n");
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
该函数没有做处理,主要调用基类的 CWinThread::Run();
int CWinThread::Run()
{
ASSERT_VALID(this);
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// dxq 获取并分发消息直到收到一个WM_QUIT 消息
for (;;)
{
// dxq 第一阶段:检查是否可以在空闲做一些工作
while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// 当为空闲时调用OnIdle
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
// 第二阶段:当可以从队列中得到消息时提取消息
do
{
// 提取消息当为WM_QUIT 时退出
if (!PumpMessage())
return ExitInstance();
// 提取正常消息,重置空闲状态
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
通过该函数的实现,可以理解消息循环的过程,和 SCK 不太一样,它是封装在MFC 下的,
实际上,它在PumpMessage 函数的定义中,学员下去可以自己看看CWinThread 中的
PumpMessage 函数。这个函数需哎哟首先从消息队列中提取消息, 接着调用
PreTranslateMessage 虚函数过滤窗口消息。此处不详细讲解啦。
5.MFC 的终止过程
应用处挂起状态时,如果不小心单击了“关闭”按钮,或者用键盘或鼠标从系菜单中选择关
闭,这时,系统都会给窗口过程发送一个WM_SYSCOMMAND 消息,窗口过程将这个消息
传给默认的窗口过程,而默认的窗口过程会给窗口过程发送一个WM_CLOSE 消息来响应,
窗口过程再次将它传给默认的窗口过程,默认窗口过程调用DestroyWindow 来响应这个
WM_CLOSE 消息, 此后, DestroyWindow 将导致Windows 给窗口过程发送一个
WM_DESTROY 消息,此消息导致窗口过程再调用PostQuitMessage,将一个WM_QUIT 消
息置入消息队列中,以此来响应此消息,Run 函数收到WM_QUIT 消息后,会结束内部的
消息循环,然后调用ExitInstance()函数,最后回到AfxWinMain(),执行AfxWinTerm,
以此来终止程序的运行。
6.MFC 程序流程小结
1. Windows 将用户程序装入内存。
2. 构造全局对象theApp,在程序被装入时,所有全局对象都会立刻被创建。
3. Windows 调用全局函数WinMain,它是类库的惟一实例
4. WinMain 里面只调用函数AfxWinMain,
5. AfxWinMain 执行AfxWininit,调用AfxinitThred,接着
6. AfxWinMain 执行InitApplication,然后执行Initinstance,Initinstance 是CWinApp 的虚函
数,在此改写。
7. InitInstance 函数里面启动文档的装入以及主要框架和视图显示处理过程。
8 .在这里new 一个CMyFrameWnd ,CMyFrameWnd 构造函数调用Create 产生主窗口
9. InitInstance 执行ShowWindow,UpdateWindow,发出WM_PAINT
10. WinMain 调用theApp 的Run 函数,它启动窗口消息和命令消息的传递处理过程。
11:单击file/close,则发出WM_CLOSE
12:CMainFrame 交默认处理
13:调用::DestroyWindow 发出WM_DESTROY
14 :默认处理调用::postQuitMessage 发出WM_QUIT
15: CWinapp::Run 收到WM_QUIT 结束内部循环,调用ExitInsance(若CCExcmpleApp 改写
Exitinstance,则调用CCExcmpleApp::ExitInstance;
16. ExitInstance 函数负责完成应用程序结束之前的清除工作。
17 . ExitInstance 函数返回时,Run 函数也返回了,MFC 完成了一些清除工作,Windows
终止应用程序
18. 回到AfxWinMain,执行AfxWinTerm,程序结束!!
5.文档/视图类
文档视图类以及主框架之间的关系
文档视图较好的实现了数据显示和数据操作的分离,具体的说,用户对数据所做的任何改变
的都是由文档类负责管理的,而视图通过调用此接口,以实现对数据的访问和更新。
框架窗口,文档,视图他们之间的关系如下图所示:
从上图可以看到,视图占据了框架窗口的客户区,框架窗口只是相当于视图的容器。这样,
即使直接在框架窗口的客户区内执行绘制操作,在屏幕上也不会由任何的输出信息,输出被
视图所覆盖。必须通过视图显示应用输出。
注意:文档至少应有一个相关的视图,相反,视图只能与一个文档向关联。
CCExcmpleDoc 类的基类为CDocument 类,CDocument 类为用户定义的文档类提供了基本
的功能,框架通过使用CDocument 提供的接口来操作文档,用户通过与文档相关联的CView
对象来与之交互。
CCExcmpleView 类的基类为CView,CView 类为用户定义的视图类CCExcmpleView 提供了
基本功能,视图是数据的用户窗口,为用户提供了文档数据的可视显示,它在窗口中显示文
档的内容,视图还给用户提供了一个与文档中的数据交互的界面,它把用户的输入转换为对
文档中的数据的操作。每个文档都会与一个或多个视图相关联,甚至可以与多个不同的视图
相关联。
视图类里面的几个重要函数:
CCExcmpleDoc* CCExcmpleView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CCExcmpleDoc)));
return (CCExcmpleDoc*)m_pDocument;
}
解析:每个视图对象只有一个文档与其相关联,用户可以通过该视图对象的成员函数
GetDocument 获取与其关联的文档,然后,就可以在视图类中对文档类的公有成员函数及成
员变量进行访问。
void CCExcmpleView::OnInitialUpdate()
{
CView::OnInitialUpdate();
}
解析:在视图与文档关联后,在视图显示之前,或者当用户选择了“File|New”或“File|Open”
时,框架就会调用此虚函数,它调用基类的OnInitialUpdate();函数。
void CView::OnInitialUpdate()
{
OnUpdate(NULL, 0, NULL); // initial update
}
在基类里面又调用了 OnUpdate 函数
void CView::OnUpdate(CView* pSender, LPARAM /*lHint*/, CObject* /*pHint*/)
{
ASSERT(pSender != this);
UNUSED(pSender); // unused in release builds
// dxq 使整个面板无效,同时擦除背景
Invalidate(TRUE);
}
它的功能主要包括:读取文档数据,然后对视图对象的数据成员或控制进行更新,以便反映
文档的便哈,还可以使视图的部分客户区无效,就有WM_PAINT 消息产生,从而触发对函
数OnDraw 的调用,利用更新后的文档数据对窗口进行重绘。
OnDraw 函数:此函数为虚函数,必须在派生类中重载此函数,它主要由框架来调用,以呈
现文档数据,依据其参数不同,可以分别完成屏幕显示,打印以及打印预览等任务,在MFC
应用程序中,几乎所有的绘制操作都是在OnDraw 中完成的(鼠标绘制除外)。