文章目录
一、MFC编程基础
MFC的概念和作用
-
1.什么是MFC
- MFC(Microsoft Foundation Class Library),全称为微软基础类库。它封装了Windows应用程序的各种API以及相关机制的C++类库。 2.MFC的作用
- 基于应用程序框架之上,使用提供的类库快速开发,提高项目的开发效率,减少开发周期和成本。
常用的头文件
- afx.h-将各种MFC头文件包含在内;
- afxwin.h-包含了各种MFC窗口类,如afx.h和windows.h;
- afxext.h-提供了扩展窗口类的支持,如工具栏、状态栏等。
- …
MFC的控制台程序
CWinApp theApp
这个全局对象控制程序的执行流程;main
函数不同于普通的控制台程序(详见程序启动机制);- 以Afx开头的函数为MFC库中的全局函数;
- 以 :: 开头的函数为win32的API函数。
MFC的库程序
- 使用MFC制作静态库程序;
- MFC的动态库:
- 使用静态的MFC库制作动态库程序;
- 使用动态的MFC库制作动态库程序;
- 扩展库
规则库可以被各种程序所调用,扩展库只能被MFC程序调用。
MFC的窗口程序
1.相关函数
- 单文档视图架构程序
CWinApp
- 应用程序类,负责管理应用程序的流程;
CFrameWnd
- 框架窗口类,负责管理框架窗口;
CView
- 视图窗口类,负责显示数据;
CDocument
- 文档类,负责管理数据。 - 多文档视图架构程序
CWinApp
- 应用程序类,负责管理应用程序的流程;
CMDIFrameWnd
-多文档主框架窗口类,负责管理主框架窗口;
CMDIChildWnd
-多文档子框架窗口类,负责管理子框架窗口;
CView
- 视图窗口类,负责显示数据;
CDocument
- 文档类,负责管理数据。 - 对话框应用程序
CWinApp
- 应用程序类,负责管理应用程序的流程;
CDialog
- 对话框窗口类,负责管理对话框窗口。
2.MFC库中相关类简介
CObject
类
MFC类库中绝大部分类的父类,提供了MFC类库中一些基本的机制;
对运行时类信息的支持;
对动态创建的支持;
对序列化的支持;
…CWinApp
- 应用程序类
该类是一个基类,封装了应用程序、线程等信息,通过它来继承Windows应用程序对象。应用程序对象提供了初始化应用程序(以及它的每一个实例)和运行应用程序所需的成员函数。CDocument
- 文档类
为用户定义的文档类提供了基本的函数功能,来管理数据。Frame WIndows
- 框架窗口类
封装了窗口程序组成的各种框架窗口处理函数。CSplitterWnd
- 拆分窗口类
该类提供一个分隔窗口的功能。Control Bars
- 控件条类
CContorlBar类是所有控件条类的基类,封装了各种控件资源。Dialog Boxes
- 对话框类
CDialog类是在屏幕上显示的对话框基类,封装了各种对话框资源。Views
- 视图类
CView类为用户定义的视图类提供了基本的功能,封装了各种显示窗口的资源。Controls
- 控件类
封装了各种常用的控件。Exceptions
- 异常处理类
CException是Microsoft基本类库中处理各种异常的基础库,封装了MFC中常用的各种异常处理方法。File Services
- 文件类
CFile是MFC文件类的基类,它直接提供非缓冲的二进制磁盘输入/输出设备,并直接地通过派生类支持文本文件和内存文件,封装了各种文件的I/O操作。- 绘图类
包括CDC类和CGDIObject类,为各种Windows图形设备接口(GDI)对象提供了一些基本类。 - 数据集合类
CArray/Clist/CMap…,封装了相应的数据结构的管理资源。 - 非CObject类的子类
提供了各种数据结构相关的管理实现,如CPoint、CTIME、CString等。
二、程序启动机制
WinMain入口函数
与Win32窗口程序相同,都是从WinMain入口,但是Win32中的WinMain函数由程序员自己实现,流程由程序员自己安排,而MFC库中已经实现了WinMain函数,程序的流程由MFC负责,所以在程序中不需要程序员自己实现。
CWinApp的成员
-
成员变量
- m_pMainWnd:当前应用程序的主窗口 成员虚函数
-
InitInstance:程序的初始化函数,完成了窗口创建等初始化处理;
ExitInstance:程序退出时调用,清理资源等善后工作;
Run:消息循环;
OnIdle:空闲处理函数。
执行流程
- 程序启动,构造theApp对象,调用父类CWinApp的构造函数:
- 将theApp对象的地址保存到线程状态信息中;
- 将theApp对象的地址保存到模块状态信息中;
- 进入WinMain函数,调用AfxWinMain函数。
- 进入入口函数WinMain:
- 获取应用程序类对象theApp的地址;
- 利用theApp地址调用InitApplication,初始化当前应用程序的数据;
- 利用theApp地址调用InitInstance函数初始化程序,在函数中创建窗口并显示;
- 利用theApp地址调用CWinApp的Run函数进行消息循环:
- 若没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理;
- 若获取WM_QUIT消息,退出程序。
- 程序退出,利用theApp地址调用ExitInstance虚函数实现退出后析构等善后处理工作。
程序 伪代码 如下:
/*
AFX_MODULE_STATE aaa;//当前程序模块状态信息
AFX_MODULE_THREAD_STATE bbb; //当前程序线程状态信息
*/
CWinApp::CWinApp()//构造全局对象CMyWinApp theApp
{
//获取全局变量&aaa
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
//获取全局变量&bbb
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
//将&theApp保存到bbb的一个成员中
pThreadState->m_pCurrentWinThread = this;
AfxGetThread()
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//获取&bbb
CWinThread* pThread = pState->m_pCurrentWinThread;
return pThread;//返回的为&theApp
}
//将&theApp保存到aaa的一个成员中
pModuleState->m_pCurrentWinApp = this;
AfxGetApp()
{
return AfxGetModuleState()->m_pCurrentWinApp;//返回&theApp
}
}
WinMain(...)//程序流程是不是theApp对象指导向哪走
{
AfxWinMain(...)
{
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();//这两代码都是获取&theApp地址
pApp->InitApplication();//利用theApp对象调用应用程序类成员虚函数 初始化
pThread->InitInstance();//利用theApp对象调用应用程序类成员虚函数 创建并显示窗口
pThread->Run()//利用theApp对象调用应用程序类成员虚函数 消息循环
{
for (;;)
{
while(没有消息时)
OnIdle(..);//利用theApp对象调用应用程序类成员虚函数 空闲处理
do{
if(GetMessage抓到WM_QUIT)
return ExitInstance();//程序结束前,利用theApp对象调用应用程序类成员虚函数 善后处理。
}while(...)
}
}
}
}
三、窗口创建机制
基本框架
- 定义自己的框架类(CMyFrameWnd),派生自CFrameWnd;
- 定义自己的应用程序类(CMyWinApp),派生自CWinApp,并重写父类成员虚函数InitInstance;
- 定义全局对象CMyWinApp theApp。
相关函数
- 创建钩子
HHOOK SetWindowsHookEx( int idHook, //钩子类型(WH_CBT) HOOKPROC ipfn, //钩子处理函数 HINSTANCE hMod, //应用程序实例句柄 SWORD DWThreadId//线程ID );
- 钩子处理函数
LRESULT CALLBACK CBTProc( int nCode, //钩子码(HCBT_CREATEWND) WPARAM WParam //窗口句柄 LPARAM lParam //... );
- 更改窗口处理函数
LONG_PTR SetWindowLongPtr( HWND hWnd, //窗口句柄 int nIndex, //GWLP_WNDPROC LONG_PTR dwNewLong //新的窗口处理函数名(函数地址) );
执行流程
- 加载菜单;
- 调用CWnd::CreateEx函数创建窗口:
- 调用PreCreateWindow函数设计和注册窗口类;
- 在函数内部调用AfxDeferRegisterClass函数,在这个函数中设计窗口类:
- WNDCLASS wndcls;//设计窗口类
- …
- wndcls.lpfnWndProc = DefWindowProc;//定义窗口的处理函数
- 调用_AfxRegisterWithIcon函数:
- 加载图标,并调用AfxRegisterClass函数;
- 在函数内部调用::RegisterClass win32 API函数注册窗口。
- 调用AfxHookWindowCreate函数:
- 调用SetWindowsHookEx创建WH_CBT类型的钩子,钩子的处理函数是_AfxCbtFilterHook;
- 将框架类对象地址(pFrame)保存到当前程序线程信息中。
- 调用CreateWindowEx函数创建窗口,马上调用钩子处理函数;
- 钩子处理函数_AfxCbtFilterHook:
- 将窗口句柄和框架类对象地址建立一对一的绑定关系;
- 使用SetWindowLong函数,将窗口处理的函数设置AfxWndProc;
- 当接收到消息时,进入AfxWndProc函数;
- AfxWndProc函数根据消息的窗口句柄,查询对应框架类对象的地址(pFrame);
- 利用框架类对象地址(pFrame)调用框架类成员虚函数WindowProc,完成消息的处理。
程序 伪代码 如下:
/*
//三个全局变量
AFX_MODULE_STATE aaa; //执行程序模块状态相关信息
AFX_MODULE_THREAD_STATE bbb; //执行程序线程状态相关信息
_AFX_THREAD_STATE* ccc; //线程状态相关信息
*/
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate")//函数内部this为pFrame
{
//加载菜单
CreateEx(..., NULL,...)//函数内部this为pFrame
{
CREATESTRUCT cs;
....
cs.lpszClass = NULL;//下面将更改
...
cs.hInstance = AfxGetInstanceHandle();
PreCreateWindow(cs)//设计注册窗口
{
AfxDeferRegisterClass(...)//设计窗口
{
WNDCLASS wndcls;
...
wndcls.lpfnWndProc = DefWindowProc;//下面将更改
...
_AfxRegisterWithIcon(&wndcls, "AfxFrameOrView100sd"..)
{
&wndcls->lpszClassName = "AfxFrameOrView100sd";
::RegisterClass(&wndcls)
}
}
cs.lpszClass = _afxWndFrameOrView; //"AfxFrameOrView100sd"
}
AfxHookWindowCreate(pFrame)
{
//获取全局变量&ccc(当前程序线程信息)
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
//利用Win32的API函数,埋下一个类型为WH_CBT的钩子
::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook,...);
//将自己new的框架类对象pFrame保存到全局变量ccc的一个成员中
pThreadState->m_pWndInit = pFrame;
}
::CreateWindowEx(...);//此函数一旦执行成功,立即转到钩子处理函数。
}
}
//钩子处理函数
_AfxCbtFilterHook(...wParam...)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();//获取&ccc
CWnd* pWndInit = pThreadState->m_pWndInit;//获取pFrame===pWndInit
HWND hWnd = (HWND)wParam;//刚刚创建成功的框架窗口句柄
pWndInit->Attach(hWnd) //函数内部this为pFrame,参数为窗口句柄
{
CHandleMap* pMap = afxMapHWND(TRUE)
{
//获取&bbb
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
//new了一个映射类对象,并将对象地址保存到bbb的一个成员中
pState->m_pmapHWND = new CHandleMap(..);
return pState->m_pmapHWND;//返回映射类对象地址
}
pMap->SetPermanent(m_hWnd = hWnd, pFrame)//函数内部this为pMap
{
m_permanentMap[hWnd] = pFrame;
}
}
//将窗口处理函数更改为AfxWndProc(才是真正的窗口处理函数)
(WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,AfxWndProc);
}
//以WM_CREATE消息为例,捎带想着点WM_PAINT消息
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd)
{
CHandleMap* pMap = afxMapHWND()
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//获取&bbb
return pState->m_pmapHWND;//返回就是之前保存在bbb中的映射类对象地址
}
pWnd = pMap->LookupPermanent(hWnd)//函数内部this为pMap
{
return m_permanentMap[hWnd];//返回的为pFrame
}
}
AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam)//参数pWnd===pFrame
{
pWnd->WindowProc(nMsg, wParam, lParam);//回到自己的代码
}
}
四、消息映射机制
作用
在不重写WindowProc虚函数的前提下,处理消息。
条件
- 类内必须添加声明宏:
- DECLARE_MESSAGE_MAP()
- 类外必须添加实现宏:
- BEGIN_MESSAGE_MAP(theClass, baseClass)
- END_MESSAGE_MAP()
- 当一个类具备上述两个条件,这个类就可以按照消息映射机制处理消息。
程序
实例程序如下:
#include <afxwin.h>
class CMyFrameWnd : public CFrameWnd{
DECLARE_MESSAGE_MAP()
/*
//宏展开
protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
*/
public:
LRESULT OnCreate( WPARAM wParam, LPARAM lParam );
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_MESSAGE( WM_CREATE, OnCreate )
END_MESSAGE_MAP()
/*
//宏展开
const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap()
{
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
{ WM_CREATE, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (&OnCreate)) },
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
static const AFX_MSGMAP messageMap = { &CFrameWnd::GetThisMessageMap, &_messageEntries[0] };
return &messageMap;
}
*/
LRESULT CMyFrameWnd::OnCreate( WPARAM wParam, LPARAM lParam ){
AfxMessageBox( "WM_CREATE" );
return 0;
}
class CMyWinApp : public CWinApp{
public:
virtual BOOL InitInstance( );
};
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance( )
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
宏展开
//DECLARE_MESSAGE_MAP()
protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
//BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap()
{
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
...
//END_MESSAGE_MAP()
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
static const AFX_MSGMAP messageMap = { &CFrameWnd::GetThisMessageMap, &_messageEntries[0] };
return &messageMap;
}
- GetThisMessageMap() 静态函数:
- 定义静态变量和静态数组,并返回本类静态变量的地址(获取链表头)。
- _messageEntries[] 静态数组(进程级生命周期):
- 静态数组元素结构体:
struct AFX_MSGMAP_ENTRY //静态数组每个元素的类型 { UINT nMessage; //消息ID UINT nCode; //通知码 UINT nID; //命令ID UINT nLastID; //最后一个命令ID UINT_PTR nSig; //处理消息的函数类型 AFX_PMSG pgn; //处理消息的函数名(地址) }
- 数组每个元素,保存的为消息ID和处理消息的函数名(地址)。
- 静态数组元素结构体:
- messageMap 静态变量(进程级生命周期):
- 静态变量结构体:
struct AFX_MSGMAP //静态变量的类型 { const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();//父类宏展开的静态变量地址 const AFX_MSGMAP_ENTRY* lpEntries;//本类宏展开的静态数组首地址 }
- 第一个成员,保存父类宏展开的静态变量地址(负责连接链表);
- 第二个成员,保存本类的静态数组首地址。
- 静态变量结构体:
- GetMessageMap() 虚函数:
- 返回本类静态变量地址(获取链表头)
执行流程
- 消息产生进入窗口处理函数(AfxWndProc);
- 根据已知窗口句柄,找到和它绑定在一起的框架类对象地址(pFrame);
- 利用框架类对象地址(pFrame)调用框架类成员虚函数WindowProc;
- 获取本类对应的静态变量,并到对应数组中匹配查找;
- 若没有找到,则获取父类对应的静态变量,并到对应的数组中匹配查找;
- 若找到了,调用找到的数组元素的最后一个成员(处理消息函数地址),完成消息处理。
程序 伪代码 如下:
//以WM_CREATE消息为例,捎带想着点WM_PAINT,WM_COMMAND(到了CWnd::OnWndMsg函数路线不一样)
AfxWndProc(...)
{
//获取和hWnd绑定在一起的框架类对象地址(pFrame===pWnd)
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
AfxCallWndProc(pWnd...)//参数pWnd===pFrame
{
pWnd->WindowProc(...)//函数内部this为pFrame===pWnd *************
{
OnWndMsg(...)//函数内部this为pFrame===pWnd
{
//获取本类宏站开的静态变量的地址(链表头结点)
const AFX_MSGMAP* pMessageMap = GetMessageMap();
const AFX_MSGMAP_ENTRY* lpEntry;
for (; pMessageMap->pfnGetBaseMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)())//遍历链表
{
lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0));
//如果找到返回找到的数组元素的地址,如果没找到返回NULL
if(lpEntry != NULL )
{
goto LDispatch;
}
}
LDispatch:
lpEntry->pfn; //CMyFrameWnd::OnCreate
//调用CMyFrameWnd::OnCreate函数完成消息的处理
}
}
}
}
五、菜单与工具栏
消息分类
- 标准的windows消息:ON_WM_XXX:
- ON_WM_CREATE()
- ON_WM_PAINT()
- ON_WM_MOUSEMOVE
- …
- 自定义消息:ON_MESSAGE
- ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
- 命令消息:ON_COMMAND
- ON_COMMAND(菜单项ID, 处理消息的函数名)
- 默认处理顺序:视图类->文档类->框架类->应用程序类
- 注:该消息处理路线与其它两个不一致
实例程序如下:
#include <afxwin.h>
#define WM_MYMESSAGE WM_USER+1001
class CMyFrameWnd : public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
int OnCreate( LPCREATESTRUCT pcs);
void OnPaint( );
void OnMouseMove( UINT nKey, CPoint pt );
LRESULT OnMyMessage( WPARAM wParam, LPARAM lParam );
int m_x;
int m_y;
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_WM_CREATE()
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_MESSAGE( WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
LRESULT CMyFrameWnd::OnMyMessage( WPARAM wParam, LPARAM lParam ){
CString str;
str.Format( "wParam=%d,lParam=%d", wParam, lParam );
AfxMessageBox( str );
return 0;
}
void CMyFrameWnd::OnMouseMove( UINT nKey, CPoint pt){
m_x = pt.x;
m_y = pt.y;
::InvalidateRect( this->m_hWnd, NULL, TRUE );
}
void CMyFrameWnd::OnPaint( ){
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint( this->m_hWnd, &ps );
::TextOut( hdc, m_x, m_y, "hello", 5);
::EndPaint( m_hWnd, &ps );
}
int CMyFrameWnd::OnCreate( LPCREATESTRUCT pcs ){
AfxMessageBox("WM_CREATE消息被处理");
::PostMessage( this->m_hWnd, WM_MYMESSAGE, 1, 2 );
return CFrameWnd::OnCreate( pcs );
}
class CMyWinApp : public CWinApp{
public:
virtual BOOL InitInstance( );
};
BOOL CMyWinApp::InitInstance( ){
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
菜单
-
1.菜单相关问题
-
- Win32:以 HMENU 菜单句柄代表菜单;
-
- MFC:以CMenu类代表菜单;
2.CMenu类简介
-
- 该类封装了关于菜单的各种操作成员函数,另外还封装了一个非常重要的成员变量m_hMenu(菜单句柄)。
3.菜单的使用
-
- 添加菜单资源
-
- 将菜单设置到窗口
- 利用pFrame调用Create函数时,传参;
- 在处理框架窗口的WM_CREATE消息时加载菜单:
CMenu menu menu.LoadMenu(...)
4.设置菜单项状态
- 将菜单设置到窗口
-
- 点击菜单
- Win32:
- WM_INITMENUPOPUP
- ::CheckMenuItem / ::EnableMenuItem
- MFC:
- ON_WM_INITMENUPOPUP
- CMenu::CheckMenuItem / CMenu::EnableMenuItem
- Win32:
- 点击菜单
-
- 右键菜单
- Win32:
- WM_CONTEXTMENU
- ::TrackPopupMenu
- MFC:
- ON_WM_CONTEXTMENU
- CMenu::TrackPopupMenu
- Win32:
- 右键菜单
实例程序如下:
#include <afxwin.h>
#include "resource.h"
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP( )
public:
afx_msg void OnNew( ); //测试COMMAND消息处理顺序
afx_msg int OnCreate( LPCREATESTRUCT pcs );
afx_msg void OnInitMenuPopup( CMenu *pPopup, UINT nPos, BOOL i );
afx_msg void OnContextMenu( CWnd* pWnd, CPoint pt );
public:
CMenu menu;
};
BEGIN_MESSAGE_MAP( CMyFrameWnd, CFrameWnd )
ON_COMMAND( ID_NEW, OnNew )
ON_WM_CREATE( )
ON_WM_INITMENUPOPUP()
ON_WM_CONTEXTMENU( )
END_MESSAGE_MAP( )
void CMyFrameWnd::OnContextMenu( CWnd* pWnd, CPoint pt )
{
//Win32
/*
HMENU hPopup = ::GetSubMenu(menu.m_hMenu,0);
::TrackPopupMenu( hPopup, TPM_LEFTALIGN|TPM_TOPALIGN, pt.x, pt.y,
0, this->m_hWnd, NULL );
*/
//MFC
CMenu* pPopup = menu.GetSubMenu(0);
pPopup->TrackPopupMenu( TPM_LEFTALIGN|TPM_TOPALIGN, pt.x, pt.y, this );
}
void CMyFrameWnd::OnInitMenuPopup( CMenu* pPopup, UINT nPos, BOOL i)
{
//MFC
//pPopup->CheckMenuItem( ID_NEW, MF_CHECKED );//内部调用Win32API函数
//Win32
::CheckMenuItem( pPopup->m_hMenu, ID_NEW, MF_CHECKED );
}
void CMyFrameWnd::OnNew( )
{
AfxMessageBox( "框架类处理了新建菜单项被点击" );
}
int CMyFrameWnd::OnCreate( LPCREATESTRUCT pcs )
{
menu.LoadMenu( IDR_MENU1 );
//this->SetMenu( &menu );//内部调用Win32API函数
::SetMenu( this->m_hWnd, menu.m_hMenu);
return CFrameWnd::OnCreate( pcs );
}
class CMyWinApp : public CWinApp
{
DECLARE_MESSAGE_MAP()
public:
virtual BOOL InitInstance( );
afx_msg void OnNew( );
};
BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
ON_COMMAND( ID_NEW , OnNew )
END_MESSAGE_MAP( )
void CMyWinApp::OnNew( )
{
AfxMessageBox( "应用程序类处理了新建被点击" );
}
BOOL CMyWinApp::InitInstance( )
{
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create(NULL, "MFCCreate");
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
工具栏
- 工具栏相关类
- CToolBarCtrl:父类为CWnd类,封装了关于工具栏控件的各种操作;
- CToolBar:父类为CControlBar,封装了关于工具栏的操作,以及和框架窗口的关系。
- 工具栏的使用
- 添加工具栏资源
- 创建工具栏:CToolBar::CreateEx
- 加载工具栏:CToolBar::LoadToolBar
- 设置工具栏的停靠:
- CToolBar::EnableDocking,设置工具栏可以停靠的位置;
- CFrameWnd::EnableDocking,框架窗口允许工具栏停靠的位置;
- CFrameWnd::DockControlBar,工具栏初始临时停放的位置。
实例程序如下:
#include <afxwin.h>
#include "resource.h"
#include <afxext.h>
class CMyFrameWnd : public CFrameWnd
{
DECLARE_MESSAGE_MAP( )
public:
afx_msg void OnNew( );
afx_msg void OnSet( );
afx_msg int OnCreate( LPCREATESTRUCT pcs );
public:
CToolBar toolbar;
};
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd )
ON_COMMAND( ID_SET, OnSet)
ON_COMMAND( ID_NEW, OnNew )
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnSet( )
{
AfxMessageBox( "绿色工具按钮被点击" );
}
int CMyFrameWnd::OnCreate( LPCREATESTRUCT pcs )
{
toolbar.CreateEx( this, TBSTYLE_FLAT, WS_CHILD|WS_VISIBLE|CBRS_ALIGN_TOP|CBRS_GRIPPER);
toolbar.LoadToolBar( IDR_TOOLBAR1 );
toolbar.EnableDocking( CBRS_ALIGN_ANY );
this->EnableDocking( CBRS_ALIGN_ANY );
this->DockControlBar( &toolbar, AFX_IDW_DOCKBAR_BOTTOM );
return CFrameWnd::OnCreate( pcs );
}
void CMyFrameWnd::OnNew()
{
AfxMessageBox( "新建菜单项被点击" );
}
class CMyWinApp : public CWinApp{
public:
virtual BOOL InitInstance( );
};
BOOL CMyWinApp::InitInstance(){
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create( NULL, "MFCToolBar", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault,
NULL, (CHAR*)IDR_MENU1);
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
六、运行时类信息机制
作用
在程序运行过程中可以获知对象的类的相关信息,如对象是否属于某个类。
条件
- 类必须派生自CObject;
- 类内必须添加声明宏:DECLARE_DYNAMIC( theClass );
- 类外必须添加实现宏:IMPLEMENT_DYNAMIC( theClass, baseClass);
- 当一个类具备上述三个要件后,CObject::IsKindOf函数就可以正确判断对象是否属于某个类。
程序
实例程序如下:
#include <iostream>
using namespace std;
class CAnimal : public CObject{
DECLARE_DYNAMIC( CAnimal )
};
IMPLEMENT_DYNAMIC( CAnimal, CObject )
class CDog : public CAnimal{
DECLARE_DYNAMIC( CDog )
/* public:
static const CRuntimeClass classCDog;
virtual CRuntimeClass* GetRuntimeClass() const; */
};
IMPLEMENT_DYNAMIC( CDog, CAnimal )
IMPLEMENT_RUNTIMECLASS(CDog, CAnimal, 0xFFFF, NULL, NULL)
/* AFX_COMDAT const CRuntimeClass CDog::classCDog = {
"CDog",
sizeof(class CDog),
0xFFFF,
NULL,
RUNTIME_CLASS(CAnimal),
//((CRuntimeClass*)(&CAnimal::classCAnimal)),
NULL,
NULL
};
CRuntimeClass* CDog::GetRuntimeClass() const
{
return RUNTIME_CLASS(CDog);
//return ((CRuntimeClass*)(&CDog::classCDog));
} */
int main(){
CDog yellowdog;
if( yellowdog.IsKindOf( RUNTIME_CLASS(CWnd) ) ){
cout << "yellowdog is CWnd" << endl;
}else{
cout << "yellowdog isnot CWnd" << endl;
}
return 0;
}
宏展开
//DECLARE_DYNAMIC(CDog)
public:
static const CRuntimeClass classCDog;
virtual CRuntimeClass* GetRuntimeClass() const;
//IMPLEMENT_DYNAMIC(CDog, CAnimal)
AFX_COMDAT const CRuntimeClass CDog::classCDog = {
"CDog",
sizeof(class CDog),
0xFFFF,
NULL,
RUNTIME_CLASS(CAnimal),
//((CRuntimeClass*)(&CAnimal::classCAnimal)),
NULL,
NULL
};
//IMPLEMENT_RUNTIMECLASS(CDog, CAnimal, 0xFFFF, NULL, NULL)
CRuntimeClass* CDog::GetRuntimeClass() const
{
return RUNTIME_CLASS(CDog);
//return ((CRuntimeClass*)(&CDog::classCDog));
}
- classCDog 静态变量:
- 静态变量结构体:
struct CRuntimeClass //宏展开的静态变量的类型 { LPCSTR m_lpszClassName; //类名称 int m_nObjectSize; //类大小 UINT m_wSchema; //类版本:0xFFFF CObject* (PASCAL* m_pfnCreateObject)();//动态创建机制使用,此处为NULL CRuntimeClass* m_pBaseClass;//父类宏展开静态变量的地址 CRuntimeClass* m_pNextClass;//不使用,为NULL const AFX_CLASSINIT* m_pClassInit;//不使用,为NULL }
- 保存类名和类大小等信息,以及父类静态变量地址(负责连接链表)
- 静态变量结构体:
- GetRuntimeClass() 虚函数:
- 获取本类静态变量地址(获取链表头结点)。
- 宏展开各部分作用见下图:
执行流程
- 利用对象(yellowdog)的地址调用宏展开的虚函数GetRuntimeClass(),获取本类静态变量的地址(链表头);
- 利用本类静态变量的地址(链表头)和目标进行比对;
- 若相同,则对象属于该类;
- 若不相同,则获取链表的下一个结点(父类静态变量的地址),循环比对。
程序 伪代码 如下:
yellowdog.IsKindOf( RUNTIME_CLASS(CDog))//函数内部this为&yellowdog,参数为链表头
{
//利用&yellowdog调用宏展开虚函数,获取链表头结点
CRuntimeClass* pClassThis = GetRuntimeClass();
//函数内部this为链表头,参数也为链表头
return pClassThis->IsDerivedFrom(RUNTIME_CLASS(CDog))
{
const CRuntimeClass* pClassThis = this;//获取链表头
while (pClassThis != NULL)
{
if (pClassThis == RUNTIME_CLASS(CDog))
return TRUE;
pClassThis = pClassThis->m_pBaseClass;
}
return FALSE;
}
}
七、动态创建机制
作用
在 不知道类名 的情况下,将类的对象创建出来。
注:实际是通过基础代码向后兼容创建类对象。
条件
- 类必须派生自CObject;
- 类内必须添加声明宏:DECLARE_DYNCREATE( theClass );
- 类外必须添加实现宏:IMPLEMENT_DYNCREATE( theClass, baseClass);
- 当一个类具备上述三个要件后,CRuntime::CreateObject(对象加工厂)函数就可以将类的对象创建出来。
程序
实例程序如下:
#include <afxwin.h>
#include <iostream>
using namespace std;
class CAnimal : public CObject{
DECLARE_DYNAMIC( CAnimal )
};
IMPLEMENT_DYNAMIC( CAnimal, CObject )
class CDog : public CAnimal{
DECLARE_DYNCREATE( CDog )
/* public:
static const CRuntimeClass classCDog;
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* PASCAL CreateObject(); */
};
IMPLEMENT_DYNCREATE( CDog, CAnimal )
/* CObject* PASCAL CDog::CreateObject()
{
return new CDog;
}
AFX_COMDAT const CRuntimeClass CDog::classCDog = {
"CDog",
sizeof(class CDog),
0xFFFF,
CDog::CreateObject,
RUNTIME_CLASS(CAnimal),
NULL,
NULL
};
CRuntimeClass* CDog::GetRuntimeClass() const
{
return RUNTIME_CLASS(CDog);
} */
int main(){
CObject* pob = RUNTIME_CLASS(CDog)->CreateObject( );//对象加工厂
if( pob ){
cout << pob << endl;
}else{
cout << "对象创建失败" << endl;
}
return 0;
}
宏展开
//DECLARE_DYNCREATE( CDog )
public:
static const CRuntimeClass classCDog;
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* PASCAL CreateObject();
//IMPLEMENT_DYNCREATE( CDog, CAnimal )
CObject* PASCAL CDog::CreateObject()
{
return new CDog;
}
AFX_COMDAT const CRuntimeClass CDog::classCDog = {
"CDog",
sizeof(class CDog),
0xFFFF,
CDog::CreateObject,
RUNTIME_CLASS(CAnimal),
NULL,
NULL
};
CRuntimeClass* CDog::GetRuntimeClass() const
{
return RUNTIME_CLASS(CDog);
}
与运行时类信息机制的区别
- 多了一个静态函数
CObject* PASCAL CDog::CreateObject() { return new CDog; }
- 静态变量的第四个成员不再为NULL,而为新增加的静态函数的地址。
执行流程
- 利用本类(CDog)的静态变量,调用CRuntimeClass的成员函数CreateObject(对象加工厂函数);
- 获取静态变量的第四个成员(新增加静态函数),并调用;
- 新增加的静态函数内部,完成类对象的创建,并返回对象地址。
程序 伪代码 如下:
RUNTIME_CLASS(CDog)->CreateObject()//函数内部this为本类(CDog)的静态变量地址(链表头)
{
CObject* pObject = (*m_pfnCreateObject)() //CDog::CreateObject
{
return new CDog;
}
return pObject;
}
八、视图和文档
视图
作用
CView的父类为CWnd类,该类封装了关于视图窗口的各种操作,以及和文档类的数据交互操作,提供了一个用于显示数据的窗口。
使用
- 定义一个自己的视图类(CMyView),派生自CView,并重写父类成员纯虚函数OnDraw;
- 应用程序类与框架类同上;
- 在处理框架窗口的WM_CREATE消息时,定义CMyView类对象,并调用Create函数创建视图窗口,视图窗口的ID为:AFX_IDW_PANE_FIRST。
- 视图窗口类对象关系图如下:
- 对象关系:
m_pMainWnd = pFrame; m_pViewActive = pView; thApp ->m_pMainWnd (pFrame框架类对象地址) ->m_pViewActive (pView视图类对象地址)
程序
实例程序如下:
#include <afxwin.h>
#include "resource.h"
class CMyView : public CView{
DECLARE_MESSAGE_MAP( )
public:
void OnDraw(CDC* pDC);
afx_msg void OnPaint( );
afx_msg void OnNew( );
};
BEGIN_MESSAGE_MAP( CMyView, CView )
ON_COMMAND( ID_NEW, OnNew )
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyView::OnNew( ){
AfxMessageBox( "视图类处理了WM_COMMAND消息" );
}
void CMyView::OnPaint( ){
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint( this->m_hWnd, &ps );
::TextOut( hdc, 200, 200, "CMyView::OnPaint", strlen("CMyView::OnPaint") );
::EndPaint( this->m_hWnd, &ps );
}
void CMyView::OnDraw( CDC* pDC ){
pDC->TextOut( 100, 100, "CMyView::OnDraw" );
}
class CMyFrameWnd : public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnPaint( );
afx_msg int OnCreate( LPCREATESTRUCT pcs );
afx_msg void OnNew( );
};
BEGIN_MESSAGE_MAP( CMyFrameWnd, CFrameWnd )
ON_WM_PAINT( )
ON_COMMAND( ID_NEW, OnNew )
ON_WM_CREATE( )
END_MESSAGE_MAP( )
void CMyFrameWnd::OnNew( ){
AfxMessageBox( "框架类处理了WM_COMMAND消息" );
}
int CMyFrameWnd::OnCreate( LPCREATESTRUCT pcs ){
CMyView* pView = new CMyView;
pView->Create( NULL, "MFCView", WS_CHILD|WS_VISIBLE|WS_BORDER, CRect(0,0,200,200), this,
AFX_IDW_PANE_FIRST );
this->m_pViewActive = pView;
return CFrameWnd::OnCreate( pcs );
}
void CMyFrameWnd::OnPaint( ){
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint( this->m_hWnd, &ps );
::TextOut( hdc, 100, 100, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint( this->m_hWnd, &ps );
}
class CMyWinApp : public CWinApp{
DECLARE_MESSAGE_MAP( )
public:
virtual BOOL InitInstance( );
afx_msg void OnNew( );
};
BEGIN_MESSAGE_MAP(CMyWinApp,CWinApp)
ON_COMMAND( ID_NEW, OnNew )
END_MESSAGE_MAP( )
void CMyWinApp::OnNew( ){
AfxMessageBox( "应用程序类处理了WM_COMMAND消息" );
}
BOOL CMyWinApp::InitInstance( ){
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->Create( NULL, "MFCView", WS_OVERLAPPEDWINDOW,
CFrameWnd::rectDefault, NULL, (CHAR*)IDR_MENU1);
this->m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
文档
作用
CDocument文档类,提供了一个用于管理数据的类,封装了关于数据的管理(数据提取、数据转换、数据存储等),并和视图类进行数据交互。
使用
- 定义一个自己的文档类(CMyDoc),派生自CDocument;
- 应用程序类、框架类以及视图类同上;
- 文档类对象关系图如下:
- 对象关系:
thApp ->m_pMainWnd (框架类对象地址pFrame) ->m_pViewActive (视图类对象地址pView) ->m_pDocument (文档类对象地址pDoc) ->m_viewList (所有视图类列表对象地址)
执行流程
- 利用框架类对象地址(pFrame)调用LoadFrame函数,创建框架窗口;
- 在处理框架窗口的WM_CREATE消息时,动态创建视图类对象,并创建视图窗口;
- 在处理视图窗口的WM_CREATE消息时,将文档类对象和视图类对象建立关联关系。
程序 伪代码 如下:
CMyFrameWnd* pFrame = new CMyFrameWnd;//视图类实例
CMyDoc* pDoc = new CMyDoc;文档类实例
CCreateContext cct;
cct.m_pCurrentDoc = pDoc;//文档类对象地址
cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView
//创建框架窗口
pFrame->LoadFrame(...,&cct)
{
Create(...,&cct)
{
CreateEx(...&cct)
{
CREATESTRUCT cs;
....
cs.lpCreateParams = &cct;
::CreateWindowEx(...,&cct);//创建主框架窗口
}
}
}
//处理框架窗口的WM_CREATE消息
CFrameWnd::OnCreate( pcs )//函数内部this为pFrame,参数可以获取::CreateWindowEx的12个参数
{
CCreateContext* pContext = (CCreateContext*)pcs->lpCreateParams;//获取&cct
OnCreateHelper(pcs, pContext)//函数内部this为pFrame,pContext===&cct
{
OnCreateClient(pcs, pContext)//函数内部this为pFrame,pContext===&cct
{
CreateView(pContext..)//函数内部this为pFrame,pContext===&cct
{
//动态创建视图类对象,并返回对象地址
CWnd* pView = pContext->m_pNewViewClass->CreateObject();
pView->Create(..,pContext)//函数内部this为pView,pContext===&cct
{
CreateEx(..,pContext)//函数内部this为pView,pContext===&cct
{
CREATESTRUCT cs;
....
cs.lpCreateParams = pContext;//pContext===&cct
::CreateWindowEx(...,pContext);//创建视图窗口
}
}
}
}
}
}
//处理视图窗口的WM_CREATE消息
CView::OnCreate( pcs )//函数内部this为pView,参数可以获取::CreateWindowEx函数的12个参数
{
CCreateContext* pContext = (CCreateContext*)lpcs->lpCreateParams;//获取&cct
pContext->m_pCurrentDoc->AddView(pView)//函数内部this为pDoc,参数为视图类对象地址
{
m_viewList.AddTail(pView);//文档类对象用一个链表成员变量,保存视图类对象地址
pView->m_pDocument = this;//视图类对象用一个普通成员变量,保存文档类对象地址
}
}
窗口切分
相关类
CSplitterWnd - 不规则框架窗口类:封装了关于不规则框架窗口的相关操作。
使用
重写CFrameWnd类的成员虚函数OnCreateClient:
- 在虚函数中调用CSplitterWnd::CreateStatic创建不规则框架窗口;
- 在虚函数中调用CSplitterWnd::CreateView创建视图窗口。
程序
实例程序如下:
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
class CMyDoc : public CDocument{
};
class CMyView : public CView{
DECLARE_DYNCREATE( CMyView ) //动态创建机制
DECLARE_MESSAGE_MAP( ) //消息映射机制
public:
virtual void OnDraw(CDC* pDC);
afx_msg int OnCreate( LPCREATESTRUCT pcs );
};
IMPLEMENT_DYNCREATE( CMyView, CView )
BEGIN_MESSAGE_MAP( CMyView, CView)
ON_WM_CREATE()
END_MESSAGE_MAP()
int CMyView::OnCreate( LPCREATESTRUCT pcs ){
return CView::OnCreate( pcs ); //将文档类对象和视图类对象建立关联关系。
}
void CMyView::OnDraw( CDC* pDC ){
pDC->TextOut( 100, 100, "我是视图窗口" );
}
class CMyFrameWnd : public CFrameWnd{
DECLARE_MESSAGE_MAP( )
public:
afx_msg int OnCreate( LPCREATESTRUCT pcs );
afx_msg void OnPaint( );
virtual BOOL OnCreateClient( LPCREATESTRUCT pcs, CCreateContext* pContext);
CSplitterWnd split;//不规则框架窗口
};
BEGIN_MESSAGE_MAP( CMyFrameWnd, CFrameWnd )
ON_WM_PAINT( )
ON_WM_CREATE( )
END_MESSAGE_MAP( )
BOOL CMyFrameWnd::OnCreateClient( LPCREATESTRUCT pcs, CCreateContext* pContext ){
//创建两个视图窗口
split.CreateStatic( this, 1, 2 );
split.CreateView( 0, 0, RUNTIME_CLASS(CMyView), CSize(100,100), pContext );
split.CreateView( 0, 1, pContext->m_pNewViewClass, CSize(100,100), pContext );
return TRUE;
}
void CMyFrameWnd::OnPaint( ){
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint( this->m_hWnd, &ps );
::TextOut( hdc, 200, 200, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint( this->m_hWnd, &ps );
}
int CMyFrameWnd::OnCreate( LPCREATESTRUCT pcs ){
return CFrameWnd::OnCreate( pcs );//动态创建视图类对象,并创建视图窗口
}
class CMyWinApp : public CWinApp{
public:
virtual BOOL InitInstance( );
};
BOOL CMyWinApp::InitInstance( ){
CMyFrameWnd* pFrame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;
CCreateContext cct;
cct.m_pCurrentDoc = pDoc;//文档类对象地址
cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView
pFrame->LoadFrame( IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct);
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
文档与视图关系
命令消息处理顺序
- 默认处理顺序:视图类->文档类->框架类->应用程序类。
- 命令消息关系图:
- 程序 伪代码 如下:
//WM_COMMAND消息的路线 OnCommand(wParam, lParam)//函数内部this为pFrame { CWnd::OnCommand(...)//函数内部this为pFrame { OnCmdMsg(...)//函数内部this为pFrame *****CFrameWnd::OnCmdMsg 起点 { CView* pView = GetActiveView()//函数内部this为pFrame { return this->m_pViewActive;//活动视图窗口对象地址 } //1. pView->OnCmdMsg(...) ===> 终点 CCmdTarget::OnCmdMsg 此函数内部this为pView //2. m_pDocument->OnCmdMsg(...) ==> 终点CCmdTarget::OnCmdMsg 此函数内部this为文档类对象 //3. CWnd::OnCmdMsg(...) ==> 终点 CCmdTarget::OnCmdMsg 此函数内部this为pFrame //4. CWinApp* pApp = AfxGetApp();//获取&theApp pApp->OnCmdMsg(...) ==> 终点 CCmdTarget::OnCmdMsg 此函数内部this为&theApp } } }
文档与视图关系
- 视图类成员函数:获取和视图类对象关联的文档类对象,调用GetDocument()函数;
- 文档类成员函数:当文档类数据发生变化时,调用UpDataAllViews刷新和文档类对象相关联的视图类对象。
- 文档类与视图类对象关系图如下:
- 文档类对象用一个链表成员变量保存视图类对象地址;
- 视图类对象用一个普通成员变量保存文档类对象地址;
- 即一个文档类对象可以对应多个视图类对象,而一个视图类对象只能对应一个文档类对象。
程序
实例程序如下:
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
class CMyDoc : public CDocument{
DECLARE_MESSAGE_MAP( )
public:
afx_msg void OnNew( );
CString str;
};
BEGIN_MESSAGE_MAP( CMyDoc, CDocument )
ON_COMMAND( ID_NEW, OnNew )
END_MESSAGE_MAP( )
void CMyDoc::OnNew( ){
this->str = "hello world"; //接受到的数据。
// this->UpdateAllViews( NULL );//刷新和这个文档类对象(this)关联的所有视图窗口
// this->m_viewList;
POSITION pos = this->GetFirstViewPosition(); //GetFirstXXXPosition
CView* pView = this->GetNextView(pos); //GetNextXXX
this->UpdateAllViews( pView );//刷新和这个文档类对象(this)关联的除了pView指向的视图窗口
}
class CMyView : public CView{
DECLARE_DYNCREATE( CMyView ) //动态创建机制
DECLARE_MESSAGE_MAP( )
public:
virtual void OnDraw(CDC* pDC);
afx_msg int OnCreate( LPCREATESTRUCT pcs );
afx_msg void OnNew( );
};
IMPLEMENT_DYNCREATE( CMyView, CView )
BEGIN_MESSAGE_MAP( CMyView, CView)
// ON_COMMAND( ID_NEW, OnNew )
ON_WM_CREATE( )
END_MESSAGE_MAP()
void CMyView::OnNew( ){
AfxMessageBox( "视图类处理的WM_COMMAND消息" );
}
int CMyView::OnCreate( LPCREATESTRUCT pcs ){
return CView::OnCreate( pcs ); //将文档类对象和视图类对象建立关联关系。
}
void CMyView::OnDraw( CDC* pDC ){
// CMyDoc* pDoc = (CMyDoc*)this->m_pDocument;
CMyDoc* pDoc = (CMyDoc*)this->GetDocument( );
pDC->TextOut( 100, 100, pDoc->str );
}
class CMyFrameWnd : public CFrameWnd{
DECLARE_MESSAGE_MAP( )
public:
afx_msg int OnCreate( LPCREATESTRUCT pcs );
afx_msg void OnPaint( );
virtual BOOL OnCreateClient( LPCREATESTRUCT pcs, CCreateContext* pContext);
CSplitterWnd split;//不规则框架窗口
afx_msg void OnNew( );
};
BEGIN_MESSAGE_MAP( CMyFrameWnd, CFrameWnd )
// ON_COMMAND( ID_NEW, OnNew)
ON_WM_PAINT( )
ON_WM_CREATE( )
END_MESSAGE_MAP( )
void CMyFrameWnd::OnNew( ){
AfxMessageBox( "框架类处理了新建被点击" );
}
BOOL CMyFrameWnd::OnCreateClient( LPCREATESTRUCT pcs, CCreateContext* pContext ){
//创建两个视图窗口
split.CreateStatic( this, 1, 2 );
split.CreateView( 0, 0, RUNTIME_CLASS(CMyView), CSize(100,100), pContext );
split.CreateView( 0, 1, pContext->m_pNewViewClass, CSize(100,100), pContext );
m_pViewActive = (CView*)split.GetPane(0,0);
return TRUE;
}
void CMyFrameWnd::OnPaint( ){
PAINTSTRUCT ps = { 0 };
HDC hdc = ::BeginPaint( this->m_hWnd, &ps );
::TextOut( hdc, 200, 200, "我是框架窗口客户区", strlen("我是框架窗口客户区"));
::EndPaint( this->m_hWnd, &ps );
}
int CMyFrameWnd::OnCreate( LPCREATESTRUCT pcs ){
return CFrameWnd::OnCreate( pcs );//动态创建视图类对象,并创建视图窗口
}
class CMyWinApp : public CWinApp{
DECLARE_MESSAGE_MAP( )
public:
virtual BOOL InitInstance( );
afx_msg void OnNew( );
};
BEGIN_MESSAGE_MAP( CMyWinApp, CWinApp )
// ON_COMMAND( ID_NEW, OnNew )
END_MESSAGE_MAP( )
void CMyWinApp::OnNew( ){
AfxMessageBox( "应用程序类处理了WM_COMMAND消息" );
}
BOOL CMyWinApp::InitInstance( ){
CMyFrameWnd* pFrame = new CMyFrameWnd;
CMyDoc* pDoc = new CMyDoc;
CCreateContext cct;
cct.m_pCurrentDoc = pDoc;//文档类对象地址
cct.m_pNewViewClass = RUNTIME_CLASS(CMyView);//&CMyView::classCMyView
pFrame->LoadFrame( IDR_MENU1, WS_OVERLAPPEDWINDOW, NULL, &cct);
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
九、序列化机制
作用
以二进制流形式读写硬盘文件(效率很高)。
相关类
- CFile - 文件操作类,完成硬盘文件的读写操作;
class CFile //封装了关于文件读写等操作。 CFile::Open CFile::Write / Read CFile::Close CFile::SeekToBegin / SeekToEnd / Seek
- CArchive - 归档类,完成内存数据的读写操作。
class CArchive { enum Mode{store = 0, load = 1...}; Bool m_nMode; //访问方式 int m_nBufSize; //buff的大小 CFile* m_pFile; //操作的文件对象 BYTE* m_lpBufCur; //当前指向 BYTE* m_lpBufMax; //终止指向 BYTE* m_lpBufStar; //开始指向 ... }
使用
序列化机制的使用
- 创建或打开文件:CFile::Open;
- 定义归档类对象:CArchive ar;
- 数据序列化(存储/写):ar << 数据;
- 关闭归档类对象:ar.Close();
- 关闭文件:CFile::Close()。
反序列化机制的使用
- 创建或打开文件:CFile::Open;
- 定义归档类对象:CArchive ar;
- 数据反序列化(加载/读):ar >> 数据;
- 关闭归档类对象:ar.Close();
- 关闭文件:CFile::Close()。
实例程序
#include <afxwin.h>
#include <iostream>
using namespace std;
void Store( )//序列化(存储/写)数据
{
CFile file;
file.Open( "E:/.../serial.txt", CFile::modeCreate | CFile::modeWrite );
CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区,大小4096字节
long age = 18;
ar << age; //将18保存当前指向的位置,并向后移动当前指向,相应字节数。
float score = 88.5;
ar << score;//将88.5保存当前指向的位置,并向后移动当前指向,相应字节数。
CString name = "zhangsan";
ar << name;
ar.Close( );
file.Close( );
}
void Load( )//反序列化(加载/读)数据
{
CFile file;
file.Open( "E:/.../serial.txt", CFile::modeRead );
CArchive ar( &file, CArchive::load, 4096 );//归档类对象,维护缓冲区,大小4096字节
long age;
ar >> age; //当反序列化第一个数据时候,内部将文件中所有数据读入ar维护的buff中
float score;
ar >> score;//当反序列化后续数据时候,不需要到硬盘文件中读取,直接到ar维护的buff中读取
CString name;
ar >> name; //当反序列化后续数据时候,不需要到硬盘文件中读取,直接到ar维护的buff中读取
ar.Close( );
file.Close( );
cout << age << ' ' << score << ' ' << name << endl;
}
int main(){
Store( );
Load( );
return 0;
}
执行过程
序列化机制的执行过程
- ar对象维护一个缓冲区;
- 将各个数据依次序列化(存储/写)到ar对象维护的缓冲区中,并将m_lpBufCur的指针指向移动相应字节;
- 如果ar维护的缓冲区不足,则将ar维护的缓冲区的数据写入硬盘文件,并重置m_lpBufCur为开始指向;
- 当关闭ar对象时,将ar对象维护的缓冲区数据写入硬盘文件,并释放ar对象维护的缓冲区。
反序列化机制的执行过程
- ar对象维护一个缓冲区;
- 当反序列化第一个数据时,将文件数据全部读取到ar对象维护的缓冲区中,并将第一个数据反序列化到第一个变量,然后将m_lpBufCur的指针移动相应字节;
- 依次反序列化每个数据到变量中;
- 当关闭ar对象时,释放ar对象维护的缓冲区。
执行过程伪代码
/*
* 序列化执行过程伪代码,反序列化过程类似
*/
class CArchive //归档类
{
enum Mode { store = 0, load = 1…};
BOOL m_nMode; //访问方式
int m_nBufSize; //buff的大小
CFile* m_pFile; //操作的文件对象
BYTE* m_lpBufCur; //当前指向
BYTE* m_lpBufMax; //终止指向
BYTE* m_lpBufStart; //开始指向
...
}
CFile file;
file.Open( "E:/.../serial.txt", CFile::modeCreate | CFile::modeWrite );
CArchive ar(&file, CArchive::store, 4096) => CArchive::CArchive(&file,0, 4096)
{
m_nMode = CArchive::store;// 0
m_pFile = &file; //“E:/....serial.txt”
m_nBufSize = 4096;
m_lpBufStart = new BYTE[m_nBufSize];
m_lpBufCur = m_lpBufStart;
m_lpBufMax = m_lpBufStart + 4096;
}
long age = 18;
ar << age => CArchive::operator<<(age)//函数内部this为&ar
{
if (m_lpBufCur + sizeof(long) > m_lpBufMax)
{
Flush();
}
*m_lpBufCur = age;//18
m_lpBufCur += sizeof(long);
}
float score = 88.5;
ar << score => CArchive::operator<<(score)//函数内部this为&ar
{
if (m_lpBufCur + sizeof(float) > m_lpBufMax)
{
Flush();
}
*m_lpBufCur = score;//88.5
m_lpBufCur += sizeof(float);
}
CString name = "zhangsan";
ar << name => CArchive::operator<<(name)//函数内部this为&ar
{
AfxWriteStringLength(ar, 8)
{
ar << (unsigned char)nLength;//8
}
Write(name, 8)//函数内部this为&ar
{
memcpy_s(m_lpBufCur, (size_t)(m_lpBufMax - m_lpBufCur), name, 8);
m_lpBufCur += 8;
}
}
ar.Close( )//函数内部this为&ar
{
Flush()//函数内部this为&ar
{
&file->Write(m_lpBufStart, ULONG(m_lpBufCur - m_lpBufStart));
m_lpBufCur = m_lpBufStart;//重置当前指向
}
}
18 88.5 8 zhangsan
|--------------------------------------------------------------------|
| | |
m_lpBufStart m_lpBufCur m_lpBufMax
序列化类对象
本质
序列化类对象,就是序列化对象的各个成员变量。
使用
- 类必须派生自CObject;
- 类内必须添加声明宏 DECLARE_SERIAL(theClass);
- 类外必须添加实现宏 IMPLEMENT_SERIAL(theClass, baseClass, 1);
- 类内必须重写虚函数 Serialize();
- 当类满足上述三个条件后,类对象即可以序列化到文件中。
实例程序
#include <afxwin.h>
#include <iostream>
using namespace std;
class CMyDoc : public CDocument{
DECLARE_SERIAL( CMyDoc )//声明宏
/*
_DECLARE_DYNCREATE(CMyDoc) //动态创建机制的声明宏
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CMyDoc* &pOb);//操作符重载函数
*/
public:
CMyDoc(int age=0, float score=0.0, CString name="")
:m_age(age),m_score(score),m_name(name)
{}
int m_age;
float m_score;
CString m_name;
virtual void Serialize( CArchive& ar );//重载虚函数
};
IMPLEMENT_SERIAL( CMyDoc, CDocument, 1 )
/*
_DECLARE_DYNCREATE(CMyDoc) //宏展开,见第七部分动态创建机制
CArchive& AFXAPI operator>>(CArchive& ar, CMyDoc* &pOb)
{
pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(CMyDoc));
return ar;
} */
void CMyDoc::Serialize( CArchive& ar ){
if( ar.IsStoring() ){
ar << m_age << m_score << m_name;//序列化基本类型变量
}else{
ar >> m_age >> m_score >> m_name;//反序列化基本类型变量
}
}
void Store( )//序列化(存储/写)数据
{
CFile file;
file.Open("E:/.../serial.txt", CFile::modeCreate | CFile::modeWrite);
CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区,大小4096字节
CMyDoc data(18, 88.5, "zhangsan");
ar << &data; //序列化对象,就是将对象的各个成员变量序列化
ar.Close( );
file.Close( );
}
void Load( )//反序列化(加载/读)数据
{
CFile file;
file.Open( "E:/.../serial.txt", CFile::modeRead );
CArchive ar( &file, CArchive::load, 4096 );//归档类对象,维护缓冲区,大小4096字节
CMyDoc* pdata = NULL;
ar >> pdata;
ar.Close( );
file.Close( );
cout << pdata->m_age << ' ' << pdata->m_score << ' ' << pdata->m_name << endl;
}
int main(){
Store( );
Load( );
return 0;
}
执行过程详解
//序列化类对象过程
CFile file;
file.Open("E:/.../serial.txt", CFile::modeCreate | CFile::modeWrite);
CArchive ar(&file, CArchive::store, 4096);//归档类对象,维护缓冲区,大小4096字节
CMyDoc data(18, 88.5, "zhangsan");
ar << &data => operator << (ar, const &data)
{
ar.WriteObject(&data)//函数内部this为&ar
{
CRuntimeClass* pClassRef = &data->GetRuntimeClass();//文档类静态变量
WriteClass(pClassRef); //将类的相关信息(类名/类大小/类版本)存入ar维护的buff中
(&data)->Serialize(ar) //函数内部this为&data
{
ar << this->m_age << this->m_score << this->m_name; //序列化基本类型变量
}
}
}
//反序列化类对象过程
CFile file;
file.Open( "E:/.../serial.txt", CFile::modeRead );
CArchive ar( &file, CArchive::load, 4096 );//归档类对象,维护缓冲区,大小4096字节
CMyDoc* pdata = NULL;
ar >> pdata => operator >> (ar, pdata)
{
pdata = ar.ReadObject(RUNTIME_CLASS(CMyDoc))//函数内部this为&ar
{
//从文件读取类的相关信息,和RUNTIME_CLASS(CMyDoc)中信息进行比对,
//如果相同返回RUNTIME_CLASS(CMyDoc),如果不同返回NULL
CRuntimeClass* pClassRef = ReadClass(RUNTIME_CLASS(CMyDoc), ...);
//动态创建CMyDoc类的对象,并返回对象地址
CObject* pOb = RUNTIME_CLASS(CMyDoc)->CreateObject();
pOb->Serialize(ar)//函数内部this为刚刚创建的CMyDoc类对象(pOb)
{
ar >> m_age >> m_score >> m_name;//反序列化基本类型变量
}
return pOb;
}
}
十、MFC架构程序
单文档视图架构
特点
只能关联一个文档(只有一个文档类对象)。
使用
- 参与架构的类:CFrameWnd / CWinApp / CView / CDocument;
- 需要用到的类:CDocTemplate->CSingleDocTemplate(单文档模板类) / CDocManager(文档管理类);
- 参与架构的四个类,除了应用程序类CWinApp 外,其余的三个类(CFrameWnd / CView / CDocument)均需要支持动态创建机制。
程序
实例程序如下:
#include <afxwin.h>
#include "resource.h"
class CMyDoc : public CDocument{
DECLARE_DYNCREATE( CMyDoc )
};
IMPLEMENT_DYNCREATE( CMyDoc, CDocument )
class CMyView : public CView{
DECLARE_DYNCREATE( CMyView )
public:
virtual void OnDraw( CDC* pDC );
};
IMPLEMENT_DYNCREATE( CMyView, CView )
void CMyView::OnDraw( CDC* pDC ){
pDC->TextOut( 100, 100, "我是视图窗口" );
}
class CMyFrameWnd : public CFrameWnd{
DECLARE_DYNCREATE( CMyFrameWnd )
};
IMPLEMENT_DYNCREATE( CMyFrameWnd, CFrameWnd )
class CMyWinApp : public CWinApp{
public:
virtual BOOL InitInstance( );
};
BOOL CMyWinApp::InitInstance( ){
CSingleDocTemplate* pTemplate = new CSingleDocTemplate( IDR_MENU1,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMyFrameWnd),
RUNTIME_CLASS(CMyView) );
AddDocTemplate( pTemplate );
OnFileNew( );
m_pMainWnd->ShowWindow( SW_SHOW );
m_pMainWnd->UpdateWindow( );
return TRUE;
}
CMyWinApp theApp;
执行流程
- 对象关系:
theApp -> m_pDocManager(文档管理类对象地址) -> m_templateList(单文档模板类对象地址) -> CSingleDocTemplate* pTemplate -> m_pOnlyDoc //唯一的文档类对象地址 -> m_pDocClass //RUNTIME_CLASS(CMyDoc) -> m_pFrameClass //RUNTIME_CLASS(CMyFrameWnd) -> m_pViewClass //RUNTIME_CLASS(CMyView)
- OnFileNew()函数的执行过程:
- 利用theApp获取文档管理类对象地址;
- 利用文档管理类对象获取单文档模板类对象地址;
- 利用单文档模板类对象获取RUNTIME_CLASS(CMyFrameWnd),调用CreateObject(对象加工厂函数)动态创建框架类对象;
- 利用单文档模板类对象获取RUNTIME_CLASS(CMyDoc),调用CreateObject(对象加工厂函数)动态创建文档类对象;
- 利用框架类对象调用LoadFrame函数,创建框架窗口。
- 程序 伪代码 如下:
OnFileNew( )//函数内部this为&theApp { m_pDocManager->OnFileNew()//函数内部this为文档管理类对象地址 { CDocTemplate* pTemplate = m_templateList.GetHead();//取出单文档模板类对象地址 pTemplate->OpenDocumentFile()//函数内部this为单文档模板类对象地址 { OpenDocumentFile(..)//函数内部this为单文档模板类对象地址 { pDocument = CreateNewDocument()//函数内部this为单文档模板类对象地址 { //动态创建文档类对象,并返回对象地址 CDocument* pDocument = m_pDocClass->CreateObject(); AddDocument(pDocument)//函数内部this为单文档模板类对象地址 { m_pOnlyDoc = pDocument; } } pFrame = CreateNewFrame(pDocument..)//函数内部this为单文档模板类对象地址 { CCreateContext context; ... context.m_pCurrentDoc = pDocument;//文档类对象地址 context.m_pNewViewClass = m_pViewClass;//RUNTIME_CLASS(CMyView) //动态创建框架类对象,并返回对象地址 CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); pFrame->LoadFrame(...., &context);//创建框架窗口 //后续过程见**文档 - 执行流程**的伪代码 } } } } }
多文档视图架构
特点
可以管理多个文档(可以有多个文档类对象)。
使用
- 参与架构的类:CMDIFrameWnd / CMDIChildWnd / CWinApp / CView / CDocument;
- 需要用到的类:CDocTemplate->CMultiDocTemplate(多文档模板类) / CDocManager(文档管理类);
- CMDIChildWnd / CView / CDocument类需要支持动态创建机制。
程序
实例程序如下:
#include <afxwin.h>
#include "resource.h"
class CMyDoc : public CDocument{
DECLARE_DYNCREATE( CMyDoc )
};
IMPLEMENT_DYNCREATE( CMyDoc, CDocument )
class CMyView : public CView{
DECLARE_DYNCREATE( CMyView )
public:
virtual void OnDraw( CDC* pDC );
};
IMPLEMENT_DYNCREATE( CMyView, CView )
void CMyView::OnDraw( CDC* pDC ){
pDC->TextOut( 100, 100, "我是视图窗口" );
}
class CMyChild : public CMDIChildWnd{
DECLARE_DYNCREATE( CMyChild )
};
IMPLEMENT_DYNCREATE( CMyChild, CMDIChildWnd )
class CMyFrameWnd : public CMDIFrameWnd{ //自己造主框架窗口类的对象
};
class CMyWinApp : public CWinApp{//自己造应用程序类的对象
public:
virtual BOOL InitInstance( );
};
BOOL CMyWinApp::InitInstance( ){
CMyFrameWnd* pFrame = new CMyFrameWnd;
pFrame->LoadFrame( IDR_MENU1 );
m_pMainWnd = pFrame;
pFrame->ShowWindow( SW_SHOW );
pFrame->UpdateWindow( );
CMultiDocTemplate* pTemplate = new CMultiDocTemplate( IDR_MENU2,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMyChild),
RUNTIME_CLASS(CMyView));
AddDocTemplate( pTemplate );
OnFileNew( );//创建文档类对象,创建子框架类对象,创建子框架窗口,创建视图类对象,创建视图窗口,文档类对象和视图类对象关联
OnFileNew( );//创建文档类对象,创建子框架类对象,创建子框架窗口,创建视图类对象,创建视图窗口,文档类对象和视图类对象关联
OnFileNew( );//创建文档类对象,创建子框架类对象,创建子框架窗口,创建视图类对象,创建视图窗口,文档类对象和视图类对象关联
return TRUE;
}
CMyWinApp theApp;
执行流程
- 对象关系:
theApp -> m_pDocManager(文档管理类对象地址) -> m_templateList(多文档模板类对象地址) -> CMultiDocTemplate* pTemplate -> m_docList //保存多个文档类对象地址 -> m_pDocClass //RUNTIME_CLASS(CMyDoc) -> m_pFrameClass //RUNTIME_CLASS(CMyFrameWnd) -> m_pViewClass //RUNTIME_CLASS(CMyView)
- OnFileNew()函数的执行过程:
- 利用theApp获取文档管理类对象地址;
- 利用文档管理类对象获取多文档模板类对象地址;
- 利用多文档模板类对象获取RUNTIME_CLASS(CMyChild),调用CreateObject(对象加工厂函数)动态创建框架类对象;
- 利用多文档模板类对象获取RUNTIME_CLASS(CMyDoc),调用CreateObject(对象加工厂函数)动态创建文档类对象;
- 利用框架类对象调用LoadFrame函数,创建子框架窗口。
- 程序 伪代码 如下:
OnFileNew( )//函数内部this为&theApp { m_pDocManager->OnFileNew()//函数内部this为文档管理类对象地址 { CDocTemplate* pTemplate = m_templateList.GetHead();//取出多文档模板类对象地址 pTemplate->OpenDocumentFile()//函数内部this为多文档模板类对象地址 { OpenDocumentFile(...)//函数内部this为多文档模板类对象地址 { pDocument = CreateNewDocument()//函数内部this为多文档模板类对象地址 { //动态创建文档类对象,并返回对象地址 CDocument* pDocument = m_pDocClass->CreateObject(); AddDocument(pDocument)//函数内部this为多文档模板类对象地址 { m_docList.AddTail(pDoument);//将文档类对象扔到多文档模板类的链表成员中 } } pFrame = CreateNewFrame(pDocument..)//函数内部this为多文档模板类对象地址 { CCreateContext context; ... context.m_pCurrentDoc = pDocument;//文档类对象地址 context.m_pNewViewClass = m_pViewClass;//RUNTIME_CLASS(CMyView) //动态创建框子架类对象,并返回对象地址 CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); pFrame->LoadFrame(...., &context);//创建子框架窗口 //后续过程见**文档 - 执行流程**的伪代码 } } } } }
对话框架构
分类
MFC中对话框分为模态对话框与非模态对话框两种。
注:模态对话框为假模态对话框。
使用
参与架构的类:CDialog / CWinApp;
- 添加对话框资源;
- 定义一个自己的对话框类(CMyDlg),管理对话框资源,派生自CDialog或CDialogEx均可;
- 其余详见相关程序。
WIN32对话框
间接方式创建非模态对话框
- 添加对话框资源;
- 查找资源:FindResource(…);
- 加载资源:LoadResource(…);
- 锁定资源:LockResource(…);
- 创建非模态对话框:CreateDialogIndirect(…)。
销毁非模态对话框
销毁非模态对话框,使用DestroyWindow函数,不能使用EndDialog函数,因为EndDialog函数只是将非模态对话框隐藏,而没有销毁。
实例程序
#include <windows.h>
#include "resource.h"
HINSTANCE g_hInstance = 0;
INT CALLBACK DlgProc( HWND hwndlg, UINT msgID, WPARAM wParam, LPARAM lParam )
{
switch( msgID )
{
case WM_DESTROY:
MessageBox( NULL, "I'm Killed", "Infor", MB_OK );
break;
case WM_SYSCOMMAND:
if( wParam == SC_CLOSE ){
HWND hWnd = GetParent( hwndlg );//获取对话框窗口的父窗口句柄
EnableWindow( hWnd ,TRUE ); //销毁窗口重新启用父窗口
DestroyWindow( hwndlg ); //销毁非模态对话框, 不能使用EndDialog
// EndDialog( hwndlg, 1001 ); //只能隐藏非模态对对话框, 但可以销毁模态对话框
}
break;
}
return FALSE;//对话框的消息交给真正对话框窗口处理函数处理。
}
void OnNoModel( HWND hWnd )
{
EnableWindow( hWnd, FALSE );//创建子窗口禁用父窗口,呈现模态对话框假象。
//直接创建非模态对话框
//HWND hDlg = CreateDialog( g_hInstance, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DlgProc );
//间接创建非模态对话框(CreateDialog函数内部执行过程)
HRSRC hRs = FindResource( g_hInstance, MAKEINTRESOURCE(IDD_DIALOG1), RT_DIALOG ); //查找资源
HGLOBAL hGl = LoadResource( g_hInstance, hRs ); //加载资源
LPCDLGTEMPLATE pTemplate = (LPCDLGTEMPLATE)LockResource( hGl ); //锁定资源
HWND hDlg = CreateDialogIndirect( g_hInstance, pTemplate, hWnd, DlgProc ); //创建非模态对话框
ShowWindow( hDlg, SW_SHOW );
}
void OnCommand( HWND hWnd, WPARAM wParam )
{
switch(LOWORD(wParam))
{
case ID_NOMODEL:
OnNoModel( hWnd );
break;
}
}
//窗口处理函数( 自定义,处理消息)
LRESULT CALLBACK WndProc( HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam )
{
switch(msgID)
{
case WM_COMMAND:
OnCommand( hWnd, wParam );
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
}
return DefWindowProc( hWnd, msgID, wParam, lParam );
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
g_hInstance = hIns;
//注册窗口类
WNDCLASS wc = { 0 };
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);//(CHAR*)IDR_MENU1;
wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &wc );//将以上所有赋值全部写入操作系统。
//在内存创建窗口
HWND hWnd = CreateWindowEx( 0, "Main", "window", WS_OVERLAPPEDWINDOW,
100, 100, 500, 500, NULL, NULL, hIns, NULL );
//显示窗口
ShowWindow( hWnd, SW_SHOW );
UpdateWindow( hWnd );
//消息循环
MSG nMsg = { 0 };
while( GetMessage(&nMsg,NULL,0,0) )
{
TranslateMessage( &nMsg );
DispatchMessage( &nMsg );//将消息交给窗口处理函数来处理。
}
return 0;
}
非模态对话框
实例程序
#include <afxwin.h>
#include "resource.h"
class CMyDlg : public CDialog
{
DECLARE_MESSAGE_MAP()
public:
void OnZjwOK();
void OnZjwCancel( );
};
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)
ON_COMMAND( IDOK, OnZjwOK )
ON_COMMAND( IDCANCEL, OnZjwCancel )
END_MESSAGE_MAP()
void CMyDlg::OnZjwCancel( )
{
// CDialog::OnCancel( );
this->DestroyWindow( );
}
void CMyDlg::OnZjwOK()
{
// CDialog::OnOK();//只能将无模式对话框隐藏,并没有销毁。
::DestroyWindow( this->m_hWnd );
}
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance( );
};
BOOL CMyWinApp::InitInstance( )
{
CMyDlg* pdlg = new CMyDlg;
pdlg->Create( IDD_DIALOG1 );
m_pMainWnd = pdlg;
pdlg->ShowWindow( SW_SHOW );
return TRUE;
}
CMyWinApp theApp;
执行过程
- 程序启动进入入口函数WinMain;
- 获取应用程序类对象theApp的地址;
- 利用theApp地址调用InitApplication,初始化当前应用程序的数据;
- 利用theApp地址调用InitInstance函数,在函数中创建非模态对话框并显示;
- 利用theApp地址调用CWinApp的Run函数进行消息循环:
- 如果没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理;
- 当对话框销毁(调用DestroyWindow),消息循环退出;
- 退出前利用theApp地址调用ExitInstance虚函数实现退出后的处理工作。
- 程序伪代码如下:
AFX_MODULE_STATE aaa; //当前程序模块状态信息 AFX_MODULE_THREAD_STATE bbb;//当前程序线程状态信息 CWinApp::CWinApp()//构造全局对象CMyWinApp theApp { //获取全局变量&aaa AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); //获取全局变量&bbb AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; //将&theApp保存到bbb的一个成员中 pThreadState->m_pCurrentWinThread = this; AfxGetThread() { AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//获取&bbb CWinThread* pThread = pState->m_pCurrentWinThread; return pThread;//返回的为&theApp } pModuleState->m_pCurrentWinApp = this;//将&theApp保存到aaa的一个成员中 AfxGetApp() { return AfxGetModuleState()->m_pCurrentWinApp;//返回&theApp } } //进入入口函数 WinMain(...) { AfxWinMain(..) { CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp();//获取&theApp pApp->InitApplication(); //利用theApp调用应用程序类的成员 虚函数(初始化) pThread->InitInstance() //利用theApp调用应用程序类的成员 虚函数(创建并显示无模式对话框) { CMyDlg* pdlg = new CMyDlg; pdlg->Create( IDD_DIALOG1 )//函数内部this为pdlg(自己new的对话框类对象地址) { CDialog::Create(MAKEINTRESOURCE(IDD_DIALOG1), ..)//函数内部this为pdlg { //以下两行代码,查找并加载对话框资源 HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG); HGLOBAL hTemplate = LoadResource(hInst, hResource); CreateIndirect(MAKEINTRESOURCE(IDD_DIALOG1), ...) { LPCDLGTEMPLATE lpDialogTemplate = LockResource(hDialogTemplate); CreateIndirect(lpDialogTemplate..) { CreateDlgIndirect(...) { ::CreateDialogIndirect(...);//以间接方式创建无模式对话框 } } } } } } pThread->Run()//函数内部this为&theApp { CWinThread::Run()//函数内部this为&theApp { for (;;) { while(没有消息时) { OnIdle(..);//空闲处理(虚函数) } do { if(::GetMessage抓到WM_QUIT消息) return ExitInstance(..);//善后处理(虚函数) }while(...) } } } } } //点击OK发出WM_COMMAND消息 CDialog::OnOK() { EndDialog(IDOK) { ::EndDialog(m_hWnd, nResult);//只能将无模式对话框隐藏 } }
模态对话框
实例程序
#include <afxwin.h>
#include "resource.h"
class CMyDlg : public CDialog
{
public:
DECLARE_MESSAGE_MAP()
enum{IDD=IDD_DIALOG1};
public:
afx_msg void OnOK();
CMyDlg(): CDialog(IDD){}
};
BEGIN_MESSAGE_MAP( CMyDlg, CDialog )
ON_COMMAND(IDOK, OnOK)
END_MESSAGE_MAP()
void CMyDlg::OnOK()
{
CDialog::OnOK();
}
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance()
{
CMyDlg dlg; //栈空间
m_pMainWnd = &dlg;
int nRet = dlg.DoModal();
return FALSE;//不再执行MFC库中安排的Run函数
}
CMyWinApp theApp;
执行过程
- 程序启动进入入口函数WinMain;
- 获取应用程序类对象theApp的地址;
- 利用theApp地址调用InitApplication,初始化当前应用程序的数据;
- 利用theApp地址调用InitInstance函数,在函数中调用DoModal函数;
- DoModal内部执行过程:
- 将父窗口设置为不可用;
- 创建非模态对话框;
- 进入消息循环;
- 退出消息循环(父类的OnOK / OnCancel导致循环退出);
- 将父窗口设置为可用状态;
- 销毁非模态对话框。
- 不再执行CWinApp的Run函数进行的消息循环。
- 程序伪代码如下:
AFX_MODULE_STATE aaa; //当前程序模块状态信息 AFX_MODULE_THREAD_STATE bbb;//当前程序线程状态信息 CWinApp::CWinApp()//构造全局对象CMyWinApp theApp { //获取全局变量&aaa AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); //获取全局变量&bbb AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; //将&theApp保存到bbb的一个成员中 pThreadState->m_pCurrentWinThread = this; AfxGetThread() { AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//获取&bbb CWinThread* pThread = pState->m_pCurrentWinThread; return pThread;//返回的为&theApp } pModuleState->m_pCurrentWinApp = this;//将&theApp保存到aaa的一个成员中 AfxGetApp() { return AfxGetModuleState()->m_pCurrentWinApp;//返回&theApp } } //进入入口函数 WinMain(...) { AfxWinMain(..) { CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp();//获取&theApp pApp->InitApplication(); //利用theApp调用应用程序类的成员 虚函数(初始化) pThread->InitInstance() { CMyDlg dlg===CDialog(IDD)//函数内部this为&dlg { m_lpszTemplateName=MAKEINTRESOURCE(IDD)//将对话框资源ID保存dlg的一个成员中 } m_pMainWnd = &dlg; dlg.DoModal()//函数内部this为&dlg { //以下三行代码,查找加载并锁定对话框资源 HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG); hDialogTemplate = LoadResource(hInst, hResource); lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate); HWND hWndParent = PreModal();//获取父窗口的句柄 ::EnableWindow(hWndParent, FALSE);//将父窗口设置为不可用状态 CreateDlgIndirect(...);//间接方式创建非模态对话框 RunModalLoop(...)//函数内部this为&dlg { for (;;) //消息循环 { while(没有消息){ 空闲处理 } do{ //消息循环的相关函数; if (!ContinueModal())//函数内部this为&dlg(m_nFlags(24/8) & 0x0010) goto ExitModal; }while(....); } ExitModal: return m_nModalResult; } } ::EnableWindow(hWndParent, TRUE);//将父窗口设置为可用 DestroyWindow();//销毁非模态对话框 return m_nModalResult;//dlg的一个成员变量 } } } //父类处理点击OK按钮发出的WM_COMMAND消息 CDialog::OnOK()//函数内部this为&dlg { EndDialog(IDOK) //参数为1 { EndModalLoop(IDOK)//函数内部this为&dlg { m_nModalResult = IDOK; // 1 m_nFlags = 8; } ::EndDialog(m_hWnd, nResult);//只能将无模式对话框隐藏 } } //父类处理点击Cancel按钮发出的WM_COMMAND消息 CDialog::OnCancel()//函数内部this为&dlg { EndDialog(IDCANCEL) //参数为2 { EndModalLoop(IDCANCEL)//函数内部this为&dlg { m_nModalResult = IDCANCEL; // 2 m_nFlags = 8; } ::EndDialog(m_hWnd, nResult);//只能将无模式对话框隐藏 } }
十一、绘图
Window下绘图需要使用绘图设备,在WIN32中使用绘图设备句柄 (HDC),在MFC中使用类对象完成相关操作。
相关类
- CDC类(绘图设备类):封装了各种绘图相关的函数,以及两个非常重要的成员变量m_hDC和m_hAttrubDC
- CPaintDC类,封装了在WM_PAINT消息中绘图的绘图设备;
- CClientDC类,封装了在客户区绘图的绘图设备。
- CGDIObject类(绘图对象类):封装了各种绘图对象相关的操作,以及一个非常重要的成员变量m_hObject(绘图对象句柄)
- CPen类,封装了画笔的相关操作;
- CBrush类,封装了画刷的相关操作;
- CFont类,封装了字体的相关操作;
- CBitmap类,封装了位图的相关操作。
实例程序
//CPaintDC
void CMFCDrawView::OnPaint()
{
CPaintDC dc(this); //::BeginPaint
dc.Rectangle( 100, 100, 300, 300 ); //绘制矩形
// ::Rectangle( dc.m_hDC, 100, 100, 300, 300 );
}
//CClientDC
void CMFCDrawView::OnClient()
{
CClientDC dc(this);
dc.Ellipse( 300, 300, 500, 500 ); //绘制椭圆
}
//CPen
void CMFCDrawView::OnPen()
{
CClientDC dc( this );
CPen pen(PS_SOLID, 2, RGB(255,0,0));
CPen* oldpen = dc.SelectObject( &pen );
// HGDIOBJ nOldPen = ::SelectObject( dc.m_hDC, pen.m_hObject );
dc.Rectangle( 100, 100, 300, 300 );
// ::Rectangle( dc.m_hDC, 100, 100, 300, 300 );
dc.SelectObject( oldpen );
// ::SelectObject( dc.m_hDC, nOldPen );
pen.DeleteObject( );
// ::DeleteObject( pen.m_hObject );
}
//CBrush
void CMFCDrawView::OnBrush()
{
CClientDC dc(this);
CBrush brush(RGB(0,255,0));
CBrush* oldbrush = dc.SelectObject( &brush );
dc.Rectangle( 100, 100, 300, 300 );
dc.SelectObject( oldbrush );
brush.DeleteObject( );
}
//CFont
void CMFCDrawView::OnFont()
{
CClientDC dc( this );
CFont font;
font.CreatePointFont(300, "黑体"); //::CreateFont(..............)
CFont* oldfont = dc.SelectObject( &font );
dc.TextOut( 100, 100, "hello");
dc.SelectObject( oldfont );
font.DeleteObject();
}
//CBitmap
void CMFCDrawView::OnBmp()
{
//添加位图资源(操作资源无需写代码)
//创建一个和当前DC,相匹配的内存DC
CClientDC dc( this );
CDC memdc;
memdc.CreateCompatibleDC( &dc ); //::CreateCompatibleDC
//将位图的数据送给内存DC
CBitmap bmp;
bmp.LoadBitmap( IDB_BITMAP1 ); //::LoadBitmap
CBitmap* oldbmp = memdc.SelectObject( &bmp );//::SelectObject
//成像
dc.BitBlt( 100, 100, 48, 48, &memdc, 0, 0, SRCCOPY );//::BitBlt
memdc.SelectObject( oldbmp );//::SelectObject
//销毁位图
bmp.DeleteObject( );//::DeleteObject
//销毁内存DC
memdc.DeleteDC( );//::DeleteDC
}
部分执行过程详解
CPaintDC dc(pView) => CPaintDC::CPaintDC(...)//函数内部this为&dc
{
Attach(::BeginPaint(pView->m_hWnd, &m_ps))//函数内部this为&dc
{
m_hDC = hDC;//将BeginPaint获取的绘图设备句柄,保存到dc对象的一个成员变量中
SetAttribDC(m_hDC)//函数内部this为&dc
{
m_hAttribDC = m_hDC;//dc对象的另一个成员变量也保存了绘图设备句柄
}
}
}
CPen pen(PS_SOLID, 2, RGB(255,0,0)) => CPen::CPen(PS_SOLID, 2, RGB(255,0,0))//函数内部this &pen
{
Attach(::CreatePen(PS_SOLID, 2, RGB(255,0,0)))//函数内部this &pen
{
m_hObject = hObject;//将::CreatePen获取的画笔句柄,保存到pen对象的一个成员变量中
}
}
十二、对象和控件
对象与控件绑定
将控件窗口与类对象绑定具有两个作用:
- 与数据类对象绑定,对象和控件可以进行数据交换;
- 与控件类对象绑定,对象可以代表整个控件。
与数据类型对象绑定
- 数据类型对象和控件可以实现数据交互;
- 重写父类成员虚函数DoDataExchange(),在函数内部通过一系列的DDX_xxx函数,实现控件和数据类型对象的数据交互;
- 实现数据交互,需调用UpdateData()函数:
- UpdateData(TRUE):控件 -> 变量
- UpdateData(FALSE):变量 -> 控件
与控件类型对象绑定
- 控件类型对象和控件绑定,控件类型对象就代表了整个控件;
- 重写父类成员虚函数DoDataExchange(),在函数内部通过一系列的DDX_xxx函数,实现控件句柄和控件类型对象的绑定;
- 通过控件类型对象控制该控件。
控件消息的处理
WM_COMMAND消息中参数:
- LOWORD(wParam) - 菜单项ID,控件ID
- HIWORD(wParam) - 菜单项:为0;控件:通知码
- IParam - 均无用
基本控件的使用
- 控件如何与数据类型对象绑定;
- 控件如何与控件类型对象绑定;
- 控件的消息如何处理。
理解了以上三点,则掌握了控件的使用方法,具体控件的使用方法,见官方帮助文档。