文章目录
- 1、MFC类库常用头文件
- 2、MFC程序分类
- 3、第一个MFC程序
- 4、执行流程
- 5、钩子函数
- 6、处理消息
- 7、消息映射机制
- 8、消息的分类
- 9、MFC菜单
- 10、工具栏
- 11、运行时类信息机制
- 12、动态创建机制
- 13、MFC视图和文档
- 14、文档类
- 15、单文档视图架构
- 16、多文档
- 17、MFC绘图
- 18、序列化机制
- 19、Win32对话框回顾
- 20、对话框架构
- 21、对象和控件绑定
- 22、控件消息的处理
- 23、基本控件
- 24、文件搜索类
1、MFC类库常用头文件
1.1、afx.h
afx.h
各种MFC头文件包含在内。
1.2、afxwin.h
afxwin.h
包含了各种MFC窗口类。包含了afx.h
和windows.h
。
1.3、afxext.h
afxext.h
提供了窗口类的支持,例如工具栏、状态栏。
2、MFC程序分类
2.1、MFC控制台程序
2.1.1、和win32控制台程序的差别
- main函数不同于普通控制台程序
CWinApp theApp;
多了一个全局对象- 以
Afx
开头为MFC库中的全局函数 - 以
::
开头为win32的API函数
2.1.2、MFC控制台程序的创建
一、创建MFC控制台应用程序
二、字符集改为多字符集
2.1、MFC库制作静态库程序
2.1、MFC动态程序
以下两种可以认为是规则库:
- 可以使用静态的MFC库制作自己的动态库
- 也可以使用动态的MFC库制作自己的动态库
这里选的是使用动态的MFC库制作自己的动态库
2.3、 MFC扩展库
- 规则库可以被各种程序所调用,扩展库只能被MFC程序调用。
- 扩展库扩展的是MFC的库
2.4、MFC的窗口程序
2.4.1、单文档视图架构程序
CWinApp
应用程序类,负责管理框架窗口CFrameWnd
框架窗口类,负责管理框架窗口CView
视图窗口类,负责显示数据CDocument
文档类,负责管理数据
2.4.2、多文档视图架构程序
CWinApp
应用程序类CMDIFrameWnd
多文档主框架窗口类CMDIChildWnd
多文档子框架窗口类CView
视图窗口类,负责显示数据CDocument
文档类,负责管理数据
2.4.3、对话框应用程序
CWinApp
应用程序类CDialog
对话框窗口类
3、第一个MFC程序
- 头文件为
<afxwin.h>
。 - setting中设置使用MFC库。
- Win32程序和MFC程序区别仅仅为能不能使用MFC库。
一、创建一个空项目的Windows应用程序
二、设置使用MFC库,这里设置使用静态库
三、创建MFCBase.cpp
,内容如下。
#include <afxwin.h>
//定义自己的框架类,派生自CFrameWnd
class CMyFrameWnd:public CFrameWnd{
};
/*
定义自己的应用程序类,派生自CWinApp类
并定义构造以及重写InitInstance虚函数
*/
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//定义CMyWinApp类的对象
CMyWinApp theApp;//程序入口
4、执行流程
- 程序启动,构造theApp对象,调用父类CWinApp的构造函数。
- 将theApp对象的地址保存到线程状态信息中
- 将theApp对象的地址保存到模块状态信息中
- 进入WinMain函数,调用AfxWinMain函数
4.1、程序启动先构造全局对象theApp
AfxGetThread()
这个全局函数是为获取theApp的地址AfxGetApp()
这个全局函数也是为获取theApp的地址
/*********************************************************************
*以下程序为伪代码无法运行
*1、程序启动先构造全局对象,即theApp
*********************************************************************/
/*
MFC库中有三个全局变量
AFX_MODULE_STATE aaa; //当前程序模块状态信息,这里我们假设这个变量名为aaa
AFX_MODULE_THREAD_STATE bbb; //当前程序线程状态信息,这里我们假设这个变量名为bbb
*/
//构造全局对象CMyWinApp theApp
CWinApp::CWinApp()
{
/*#1
1、获取全局变量的地址&aaa
2、_AFX_CMDTARGET_GETSTATE()查看定义可知是个宏
#define _AFX_CMDTARGET_GETSTATE() (AfxGetModuleState())
3、AfxGetModuleState()是为获取&aaa
*/
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
/*#2
1、获取全局变量的地址&bbb
2、上一句代码可知pModuleState即为&aaa
3、故&bbb保存在&aaa下的一个成员m_thread中
*/
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
/*#3
1、关于this:一个函数中的this,谁调用这个函数这个this就是谁
2、构造函数里的this:构造谁,构造函数里的this就是谁
3、这里是在构造theApp,故此处this就是theApp的地址
4、由上句代码可知,pThreadState为&bbb
5、故此处代码是将theApp的地址保存在&bbb的成员变量m_pCurrentWinThread中
*/
pThreadState->m_pCurrentWinThread = this;
/*#4
1、ASSERT是断言判断:断言判断括号内的判断必须成立,
2、若不成立,会弹出段错误,程序将停止运行
3、AfxGetThread()
*/
ASSERT(AfxGetThread() == this);
//#4.0进入AfxGetThread()函数,这个函数是为获取&theApp
AfxGetThread()
{
/*#4.1
1、获取&bbb
*/
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
//#4.1.0进入AfxGetModuleThreadState()内部
AfxGetModuleThreadState()
{
/*#4.1.1
1、AfxGetModuleState()是为获取&aaa
2、&aaa的成员变量m_thread保存着&bbb
*/
AFX_MODULE_THREAD_STATE* pResult=AfxGetModuleState()->m_thread.GetData();
ENSURE(pResult != NULL);
return pResult;
}
/*#4.2
1、#4.1可知pState即为&bbb
2、#3可知m_pCurrentWinThread为&bbb的成员变量,保存着theApp的地址
3、即pThread为theApp的地址
*/
CWinThread* pThread = pState->m_pCurrentWinThread;
/*#4.3
1、返回theApp的地址
*/
return pThread;
}
/*#5
1、将&theApp保存到aaa的成员变量中
*/
pModuleState->m_pCurrentWinApp = this;
/*#6
*/
ASSERT(AfxGetApp() == this);
/*#6.0
1、进入AfxGetApp(),这个函数是为获取&theApp
*/
AfxGetApp()
{
/*#6.1
1、afxCurrentWinApp查看定义可知是个宏
2、#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp
3、由#1知AfxGetModuleState()获取的是&aaa
4、#5知m_pCurrentWinApp是保存了&theApp
*/
return afxCurrentWinApp;//获取&theApp
}
}
4.2、进入WinMain函数
- 进入入口函数WinMain
- 获取应用类对象theApp对象地址
- 利用theApp地址调用
InitApplication
虚函数,初始化当前应用程序的数据 - 利用theApp地址调用
InitInstance
虚函数初始化程序,在函数中我们创建窗口并为示 - 利用theApp地址调用CWinApp的
Run
虚函数进行消息循环 - 如果没有消息,利用theApp地址调用
OnIdle
虚函数实现空闲处理 - 程序退出利用theApp地址调用
ExitInstance
虚函数实现退出前的善后处理工作
- 成员虚函数
InitApplication
初始化当前应用程序数据InitInstance
初始化函数,完成了窗口创建等初始化处理Run
消息循环OnIdle
空闲处理函数ExitInstance
程序退出时调用,清理资源等善后工作- 所有这些虚函数都可以在自己程序中重写
- 成员变量
m_pMainWnd
当前应用程序的主窗口
一、启动调试后,可以查看【调用堆栈】
二、启动调用堆栈后,窗口下方依次列出了函数的调用关系,双击第三行,进入WinMain函数
三、进入WinMain函数
_tWinMain()
{ //*以下程序为伪代码无法运行
/*#1********************************************************************
*入口函数
*********************************************************************/
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
/*#1.0****************************************************************
*进入AfxWinMain()
********************************************************************/
AfxWinMain()
{
/*#1.1*****************************************************************
*以下两行代码均获取&theApp
*********************************************************************/
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
/*#1.2*****************************************************************
*1、pApp即&theApp
*2、利用theApp对象调用应用程序类成员虚函数 初始化*****
*********************************************************************/
if (pApp != NULL && !pApp->InitApplication())
/*#1.3*****************************************************************
*1、pThread即&theApp
*2、利用theApp对象调用应用程序类成员虚函数InitInstance(),创建并显示窗口****
*3、我们程序中已经重写了InitInstance(),所以这里就回到了我们自己写的程序中
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
*********************************************************************/
if (!pThread->InitInstance())
/*#1.4*****************************************************************
*1、 利用theApp对象调用应用程序类成员虚函数 消息循环
*
*********************************************************************/
nReturnCode = pThread->Run();
/*#1.4.0*****************************************************************
*1、进入Run()函数
*
*********************************************************************/
int CWinApp::Run()
{
/*#1.4.1*********************************
*1、进入Run()函数
*****************************************/
return CWinThread::Run();
/*#1.4.1.0*********************************
*1、进入CWinThread::Run()函数
*****************************************/
CWinThread::Run()
{
for(;;)//死循环
{
whel(没有消息时)
OnIdle();//利用theApp对象调用应用程序类成员虚函数 空闲处理
/*
获取到WM_QUIT则跳出死循环
*/
if (!PumpMessage())//进入这个函数可以看到::GetMessage()、::TranslateMessage()、::DispatchMessage()
return ExitInstance();//程序结束前,利用theApp对象调用应用程序类成员虚函数(写全了应该是this->ExitInstance()) 善后处理
}
}
}
}
}
5、钩子函数
5. 1、创建钩子
HHOOK SetWindowsHookExA(
[in] int idHook,//钩子类型(WH_CBT)
[in] HOOKPROC lpfn,//钩子处理函数
[in] HINSTANCE hmod,//应用程序实例句柄
[in] DWORD dwThreadId//线程ID
);
5. 2、钩子处理函数
LRESULT CALLBACK CBTProc(
_In_ int nCode,//钩子码(HCBT_CREATEWND)
_In_ WPARAM wParam,//刚刚创建成功窗口句柄
_In_ LPARAM lParam
);
5. 3、更改窗口处理函数
LONG_PTR SetWindowLongPtrA(
[in] HWND hWnd,//窗口句柄
[in] int nIndex,//如果是GWLP_WNDPROC,则更改窗口处理函数
[in] LONG_PTR dwNewLong//新窗口的处理函数名(函数地址)
);
6、处理消息
LRESULT CMyFrameWnd::WindowProc(UINT msgID,WPARAM wParam,LPARAM lParam){
//此函数内部的this为pFrame
switch(msgID){
case WM_CREATE:
AfxMessageBox("WM_CREATE消息被处理");
break;
case WM_PAINT:
{
PAINTSTRUCT ps={0};
HDC hdc=::BeginPaint(this->m_hWnd,&ps);
::TextOutA(hdc,100,100,"hello",5);
::EndPaint(m_hWnd,&ps);
}
break;
}
return CFrameWnd::WindowProc(msgID,wParam,lParam);
}
/*
定义自己的应用程序类,派生自CWinApp类
并定义构造以及重写InitInstance虚函数
*/
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//定义CMyWinApp类的对象
CMyWinApp theApp;//程序入口
7、消息映射机制
7.1、消息映射机制的作用
- 在不重写WindowProc虚函数的前提下,仍然可以处理消息。
7.2、消息映射机制的使用
- 类必须具备的条件:
- 类内必须添加声明宏
DECLARE_MESSAGE_MAP()
- 类外必须添加实现宏
BEGIN_MESSAGE_MAP(theClass,baseClass)
END_MESSAGE_MAP()
- 类内必须添加声明宏
- 当一个类具备上述两个条件,这个类就可以按照消息映射机制来处理消息。
以WM_CREATE消息为例:
BEGIN_MESSAGE_MAP(theClass,baseClass)
和END_MESSAGE_MAP()
之间添加ON_MESSAGE(WM_CREATE,OnCreate)
宏。- 在CMyFrameWnd类内添加OnCreate函数声明和定义。
#include <afxwin.h>
//定义自己的框架类,派生自CFrameWnd
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
LRESULT OnCreate(WPARAM,LPARAM lParam);
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_MESSAGE(WM_CREATE,OnCreate)
END_MESSAGE_MAP()
LRESULT CMyFrameWnd::OnCreate(WPARAM,LPARAM lParam){
AfxMessageBox("WM_CREATE");
return 0;
}
/*
定义自己的应用程序类,派生自CWinApp类
并定义构造以及重写InitInstance虚函数
*/
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//定义CMyWinApp类的对象
CMyWinApp theApp;//程序入口
7.3、消息映射机制的实现原理
7.3.1、宏
上述代码将宏展开
#include <afxwin.h>
//定义自己的框架类,派生自CFrameWnd
class CMyFrameWnd:public CFrameWnd{
//展开DECLARE_MESSAGE_MAP()宏开始
protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
//展开DECLARE_MESSAGE_MAP()宏结束
public:
LRESULT OnCreate(WPARAM,LPARAM lParam);
};
//展开BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)宏开始
PTM_WARNING_DISABLE
const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap()
{
typedef CMyFrameWnd ThisClass;
typedef CFrameWnd TheBaseClass;
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
//展开BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)宏结束
//展开ON_MESSAGE(WM_CREATE,OnCreate)宏开始
{ WM_CREATE, 0, 0, 0, AfxSig_lwl,(AFX_PMSG)(AFX_PMSGW)
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (OnCreate))
},
//展开ON_MESSAGE(WM_CREATE,OnCreate)宏结束
//展开END_MESSAGE_MAP()宏开始
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
static const AFX_MSGMAP messageMap ={
&CFrameWnd::GetThisMessageMap, &_messageEntries[0]
};
return &messageMap;
}
PTM_WARNING_RESTORE
//展开END_MESSAGE_MAP()宏开始
LRESULT CMyFrameWnd::OnCreate(WPARAM,LPARAM lParam){
AfxMessageBox("WM_CREATE");
return 0;
}
/*
定义自己的应用程序类,派生自CWinApp类
并定义构造以及重写InitInstance虚函数
*/
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//定义CMyWinApp类的对象
CMyWinApp theApp;//程序入口
上述代码删除下面两行
typedef CMyFrameWnd ThisClass;
typedef CFrameWnd TheBaseClass;
下面两个宏也可以去掉
PTM_WARNING_DISABLE
PTM_WARNING_RESTORE
去掉后,OnCreate需改为&OnCreate
static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (OnCreate)
static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (&OnCreate)
#include <afxwin.h>
//定义自己的框架类,派生自CFrameWnd
class CMyFrameWnd:public CFrameWnd{
//展开DECLARE_MESSAGE_MAP()宏开始
protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
//展开DECLARE_MESSAGE_MAP()宏结束
public:
LRESULT OnCreate(WPARAM,LPARAM lParam);
};
//展开BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)宏开始
//PTM_WARNING_DISABLE
const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const
{
return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap()
{
//typedef CMyFrameWnd ThisClass;
//typedef CFrameWnd TheBaseClass;
static const AFX_MSGMAP_ENTRY _messageEntries[] =
{
//展开BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)宏结束
//展开ON_MESSAGE(WM_CREATE,OnCreate)宏开始
{ WM_CREATE, 0, 0, 0, AfxSig_lwl,(AFX_PMSG)(AFX_PMSGW)
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > (&OnCreate))
},
//展开ON_MESSAGE(WM_CREATE,OnCreate)宏结束
//展开END_MESSAGE_MAP()宏开始
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
static const AFX_MSGMAP messageMap ={
&CFrameWnd::GetThisMessageMap, &_messageEntries[0]
};
return &messageMap;
}
//PTM_WARNING_RESTORE
//展开END_MESSAGE_MAP()宏开始
LRESULT CMyFrameWnd::OnCreate(WPARAM,LPARAM lParam){
AfxMessageBox("WM_CREATE");
return 0;
}
/*
定义自己的应用程序类,派生自CWinApp类
并定义构造以及重写InitInstance虚函数
*/
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//定义CMyWinApp类的对象
CMyWinApp theApp;//程序入口
编译后,成功运行。
7.3.2、结构体
struct AFX_MSGMAP_ENTRY//静态数组每个元素的类型
{
UINT nMessage; //消息ID
UINT nCode; //控制代码或WM_NOTIFY通知码
UINT nID; //命令ID,菜单ID,控件ID,如果是窗口消息,其值为0
UINT nLastID; //一定范围命令的最后一个命令或控件ID,用于支持组消息映射
UINT_PTR nSig; //消息处理函数的类型
AFX_PMSG pfn; //消息处理函数名(地址)
};
struct AFX_MSGMAP//静态变量的类型
{
//父类宏展开的静态变量地址
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
//本类宏展开的静态数组首地址
const AFX_MSGMAP_ENTRY* lpEntries;
};
7.3.3、宏展开各部分的作用
- GetThisMessageMap()静态函数:定义静态变量和静态数组,并返回本类静态变量地址(获取链表头)。
- _messageEntries[]静态数组(进程级生命周期):数组每个元素,保存的为消息ID和处理消息的函数名(地址)
- messageMap静态变理(进程级生命周期):
- 第一个成员,保存父类宏展开的静态变量地址(负责连接链表)。
- 第二个成员,保存本类的静态数组首地址。
- GetMessageMap()虚函数:返回本类静态变量地址(获取链表头)
8、消息的分类
8.1、标准windows消息ON_WM_XXX
- 每个标准windows消息都有自己专属的宏,及消息处理函数名。
- 如:
WM_CREATE消息用ON_WM_CREATE()宏,消息处理函数OnCreate()
WM_PAINT消息用ON_WM_PAINT()宏,消息处理函数OnPaint()
WM_MOUSEMOVE()消息用ON_WM_MOUSEMOVE宏,消息处理函数OnMouseMove()
8.2、 自定义消息ON_MESSAGE
- 可以处理任何消息,包括标准windows消息。
- 使用
ON_MESSAGE
处理。 - 如:
ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)
#include <afxwin.h>
#define WM_MYMESSAGE WM_USER+1102
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
//LRESULT OnCreate(WPARAM,LPARAM lParam);
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()//window标准消息宏
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_MYMESSAGE,OnMyMessage)//自定义消息宏
END_MESSAGE_MAP()
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
AfxMessageBox("WM_CREATE");
::PostMessageA(this->m_hWnd,WM_MYMESSAGE,1,2);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnPaint(){
PAINTSTRUCT ps={0};
HDC hdc=::BeginPaint(this->m_hWnd,&ps);
::TextOut(hdc,m_x,m_y,"hello",5);
::EndPaint(this->m_hWnd,&ps);
}
void CMyFrameWnd::OnMouseMove(UINT nKey,CPoint pt){
m_x=pt.x;
m_y=pt.y;
::InvalidateRect(this->m_hWnd,NULL,TRUE);
}
LRESULT CMyFrameWnd::OnMyMessage(WPARAM wParam, LPARAM lParam){
CString str;
str.Format("wParam=%d,lParam=%d",wParam,lParam);
AfxMessageBox(str);
return 0;
}
/*
定义自己的应用程序类,派生自CWinApp类
并定义构造以及重写InitInstance虚函数
*/
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCBase");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
//定义CMyWinApp类的对象
CMyWinApp theApp;//程序入口
8.3、命令消息ON_COMMAND
9、MFC菜单
- Win32中菜单句柄HMENU
- MFC中用CMenu类对象
9.1、CMenu类简介
- 封装了关于菜单的各种操作成员函数,另外还封装了一个非常重要的成员变量
m_hMenu
菜单句柄。
9.1、菜单创建过程
一、添加菜单资源rc文件
二、将菜单设置到窗口
- 利用pFrame调用Create函数时,传参。
- 在处理框架窗口的WM_CREATE消息时
CMenu menu;
menu.LoadMenu(…)
9.2、菜单消息的处理
- ON_COMMAND(菜单项ID,处理消息的函数名)
#include <afxwin.h>
#include "resource.h"
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
int OnCreate( LPCREATESTRUCT pcs);
afx_msg void OnNew();
public:
CMenu menu;
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_NEW,OnNew)
END_MESSAGE_MAP()
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnNew(){
AfxMessageBox("新建菜单项被点击");
}
class CMyWinApp:public CWinApp{
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
//pFrame->Create(NULL,"MFCCreate",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,NULL,(CHAR*)IDR_MENU1);
pFrame->Create(NULL,"MFCCreate");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
};
CMyWinApp theApp;
9.3、命令消息处理顺序
ON_COMMAND消息执行顺序如下:
- 框架类CMyFrameWnd
- 应用程序类CMyWinApp
- 当CMyFrameWnd不处理ON_COMMAND消息时,该消息将由CMyWinApp处理。
- 仅限于ON_COMMAND消息,不包括WM_CREATE消息。
#include <afxwin.h>
#include "resource.h"
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
int OnCreate( LPCREATESTRUCT pcs);
afx_msg void OnNew();
public:
CMenu menu;
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
//ON_COMMAND(ID_NEW,OnNew)//框架类的消息不处理,该消息将由CMyWinApp处理
END_MESSAGE_MAP()
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnNew(){
AfxMessageBox("新建菜单项被点击");
}
class CMyWinApp:public CWinApp{
DECLARE_MESSAGE_MAP()
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
//pFrame->Create(NULL,"MFCCreate",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,NULL,(CHAR*)IDR_MENU1);
pFrame->Create(NULL,"MFCCreate");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
afx_msg void OnNew();
};
void CMyWinApp::OnNew(){
AfxMessageBox("应用程序类点击了新建菜单");
}
BEGIN_MESSAGE_MAP(CMyWinApp,CWinApp)
ON_COMMAND(ID_NEW,OnNew)//CMyFrameWnd的消息不处理,该消息将由CMyWinApp处理,仅限于ON_COMMOND消息
END_MESSAGE_MAP()
CMyWinApp theApp;
9.4、设置菜单项状态
- Win32
- WM_INITMENUPOPUP
- ::CheckMenuItem 设置菜单前打勾
- ::EnableMenuItem 设置菜单不可用
- MFC
- ON_WM_INITMENUUPOPUP
- CMenu::CheckMenuItem 设置菜单前打勾
- CMenu::EnableMenuItem 设置菜单不可用
#include <afxwin.h>
#include "resource.h"
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
int OnCreate( LPCREATESTRUCT pcs);
afx_msg void OnNew();
afx_msg void OnInitMenuPopup( CMenu *pPopup, UINT nPos, BOOL i );
public:
CMenu menu;
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
//ON_COMMAND(ID_NEW,OnNew)//框架类的消息不处理,该消息将由CMyWinApp处理
ON_WM_INITMENUPOPUP()
END_MESSAGE_MAP()
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnNew(){
AfxMessageBox("新建菜单项被点击");
}
void CMyFrameWnd::OnInitMenuPopup(CMenu *pPopup, UINT nPos, BOOL i){
pPopup->CheckMenuItem(ID_NEW,MF_CHECKED);
}
class CMyWinApp:public CWinApp{
DECLARE_MESSAGE_MAP()
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
//pFrame->Create(NULL,"MFCCreate",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,NULL,(CHAR*)IDR_MENU1);
pFrame->Create(NULL,"MFCCreate");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
afx_msg void OnNew();
};
void CMyWinApp::OnNew(){
AfxMessageBox("应用程序类点击了新建菜单");
}
BEGIN_MESSAGE_MAP(CMyWinApp,CWinApp)
ON_COMMAND(ID_NEW,OnNew)//CMyFrameWnd的消息不处理,该消息将由CMyWinApp处理,仅限于ON_COMMOND消息
END_MESSAGE_MAP()
CMyWinApp theApp;
9.5、右键菜单
- Win32
- WM_CONTEXTMENU
- ::TracckPopupMenu
- MFC
- ON_WM_CONTEXTMENU
- CMenu::TrackPopupMenu
#include <afxwin.h>
#include "resource.h"
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
int OnCreate( LPCREATESTRUCT pcs);
afx_msg void OnNew();
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_WM_CREATE()
//ON_COMMAND(ID_NEW,OnNew)//框架类的消息不处理,该消息将由CMyWinApp处理
ON_WM_INITMENUPOPUP()
ON_WM_CONTEXTMENU( )
END_MESSAGE_MAP()
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
menu.LoadMenu(IDR_MENU1);
this->SetMenu(&menu);
return CFrameWnd::OnCreate(pcs);
}
void CMyFrameWnd::OnNew(){
AfxMessageBox("新建菜单项被点击");
}
void CMyFrameWnd::OnInitMenuPopup(CMenu *pPopup, UINT nPos, BOOL i){
pPopup->CheckMenuItem(ID_NEW,MF_CHECKED);
}
void CMyFrameWnd::OnContextMenu( CWnd* pWnd, CPoint pt){
//HMENU hPopup=::GetSubMenu(menu.m_hMenu,0);
//::TrackPopupMenu(hPopup,TPM_LEFTALIGN|TPM_TOPALIGN,pt.x,pt.y,0,this->m_hWnd,NULL);
CMenu* pPopup=menu.GetSubMenu(0);
pPopup->TrackPopupMenu(TPM_LEFTALIGN|TPM_TOPALIGN,pt.x,pt.y,this);
}
class CMyWinApp:public CWinApp{
DECLARE_MESSAGE_MAP()
public:
CMyWinApp(){
}
virtual BOOL InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
//pFrame->Create(NULL,"MFCCreate",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,NULL,(CHAR*)IDR_MENU1);
pFrame->Create(NULL,"MFCCreate");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
afx_msg void OnNew();
};
void CMyWinApp::OnNew(){
AfxMessageBox("应用程序类点击了新建菜单");
}
BEGIN_MESSAGE_MAP(CMyWinApp,CWinApp)
ON_COMMAND(ID_NEW,OnNew)//CMyFrameWnd的消息不处理,该消息将由CMyWinApp处理,仅限于ON_COMMOND消息
END_MESSAGE_MAP()
CMyWinApp theApp;
10、工具栏
10.1、工具栏相关的类
CToolBarCtrl
父类CWnd
,封装了关于工具栏控件的各种操作。CToolBar
父类CControlBar
,封装了关于工具栏的操作,以及和框架窗口的关系。
10.2、工具栏的使用
- 添加工具栏资源
- 创建工具栏
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_NEW,OnNew)
ON_COMMAND(ID_SET,OnSet)
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnNew(){
AfxMessageBox("新建菜单项被点击");
}
void CMyFrameWnd::OnSet(){
AfxMessageBox("ON_SET被点击");
}
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_LEFT);
return CFrameWnd::OnCreate(pcs);
}
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;
11、运行时类信息机制
11.1、运行时类信息机制的作用
- 在程序运行过程中获知对象的类的相关信息(例如:对象是否属于某个类)。
11.1、运行时类信息机制的使用
- 类必须派生自
CObject
- 类内必须添加声明宏
DECLARE_DYANAMIC(theClaww)
- 类外必须添加实现宏
IMPLEMENT_DYNAMIC(theClass,baseClass)
- 当一个类具备上述三个条件后,CObject:::IsKindOf函数就可以正确判断对象是否属于某个类。
#include <afxwin.h>
#include <iostream>
using namespace std;
class CAnimal:public CObject{
DECLARE_DYNAMIC(CAnimal)
};
IMPLEMENT_DYNAMIC(CAnimal,CObject)
class CDog:public CAnimal{
DECLARE_DYNAMIC(CDog)
};
IMPLEMENT_DYNAMIC(CDog,CAnimal)
int main(){
CDog yellowdog;
if(yellowdog.IsKindOf(RUNTIME_CLASS(CDog)))
cout<<"yellowdog is CDog"<<endl;
else
cout<<"yellowdog isnot CDog"<<endl;
return 0;
}
11.2、RUNTIME_CLASS宏
- RUNTIME_CLASS是一个非常要的宏。
RUNTIME_CLASS(theClass)
—》&theClass::classtheClass
获取theClass这个类的静态变量地址。
12、动态创建机制
12.1、动态创建机制的作用
- 在不知道类名的情况下,将类的对象创建出来。
12.1、动态创建机制的使用用
- 类必须派生自CObject
- 类内必须添加声明宏
DECLARE_DYNCREATE(theClass)
- 类外必须添加实现宏
IMPLEMENT_DYCREATE(theClass,baseClass)
- 当一个类具备上述三个要件后,
CRuntimeClass::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)
};
IMPLEMENT_DYNCREATE(CDog,CAnimal)
int main(){
CObject* pob=RUNTIME_CLASS(CDog)->CreateObject();
if(pob)
cout<<pob<<endl;
else
cout<<"对象创建失败"<<endl;
return 0;
}
12.2、动态创建机制与运行时类信息区别
13、MFC视图和文档
13.1、视图窗口的作用
- 提供了一个用于显示数据的窗口。
13.2、视图窗口的相关类
- CView及其子类,父类为CWnd类,封装了关于视图窗口的各种操作,以及和文档类的数据交互。
13.2、视图窗口的使用
- 定义一个自己的视图类(CMyView),派生自CView,并重写父类成员纯虚函数OnDraw(可用于绘图)。
- 其余框架类和应用程序类代码不变。
- 在处理框架窗口的WM_CREATE消息时,定义CMyView类对象,并调用Create函数创建视图窗口
- 视图窗口的ID为
AFX_IDW_PANE_FIRST
#define AFX_IDW_PANE_FIRST 0xE900
- 当视图窗口的ID<0xE900,窗口大小为指定的大小,>=则平铺在框架窗口客户区。
#include <afxwin.h>
class CMyView:public CView{
public:
void OnDraw(CDC* pDC);
};
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);
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_PAINT()
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnPaint(){
PAINTSTRUCT ps={0};
HDC hdc=::BeginPaint(this->m_hWnd,&ps);
::TextOut(hdc,100,100,"框架窗口客户区",strlen("框架窗口客户区"));
::EndPaint(this->m_hWnd,&ps);
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
CMyView* pView=new CMyView;
pView->Create(NULL,"MFCCview",WS_CHILD|WS_VISIBLE|WS_BORDER,CRect(0,0,100,100),this,AFX_IDW_PANE_FIRST);
return CFrameWnd::OnCreate(pcs);
}
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCCview");
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
上述代码:
- CMyView类必须重写OnDraw虚函数。
- CMyView的父类CView在处理自己的WM_PAINT消息时,会调用CMyView类中的OnDraw函数。
- 如果CMyView中也处理WM_PAINT消息,MFC将不再处理父类CView中的WM_PAINT消息。
- 因此在CMyView类中的OnDraw函数或OnPaint函数中处理都是一样的,但是OnDraw必须写,所以直接在OnDraw函数中处理就好,不需要在处理CMyView类中的WM_PAINT消息了。
13.3、命令消息的处理顺序
视图类----框架类----应用程序类
#include <afxwin.h>
#include "resource.h"
class CMyView:public CView{
DECLARE_MESSAGE_MAP()
public:
void OnDraw(CDC* pDC);
afx_msg void OnNew();
};
BEGIN_MESSAGE_MAP(CMyView,CView)
ON_COMMAND(ID_NEW,OnNew)
END_MESSAGE_MAP()
void CMyView::OnNew(){
AfxMessageBox("视图类处理了WM_COMMAND消息");
}
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);
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_PAINT()
ON_WM_CREATE()
END_MESSAGE_MAP()
void CMyFrameWnd::OnPaint(){
PAINTSTRUCT ps={0};
HDC hdc=::BeginPaint(this->m_hWnd,&ps);
::TextOut(hdc,100,100,"框架窗口客户区",strlen("框架窗口客户区"));
::EndPaint(this->m_hWnd,&ps);
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
CMyView* pView=new CMyView;
pView->Create(NULL,"MFCCview",WS_CHILD|WS_VISIBLE|WS_BORDER,CRect(0,0,100,100),this,AFX_IDW_PANE_FIRST);
m_pViewActive=pView;
return CFrameWnd::OnCreate(pcs);
}
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->Create(NULL,"MFCCview",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,NULL,(CHAR*)IDR_MENU);
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
13.4、对象关系图
m_pMainWnd=pFrame;
m_pViewActive=pView;
- theApp
theApp->m_pMainWnd
(pFrame框架类对象的地址)m_pMainWnd->m_pViewActive
(pView视图类的对象地址)
14、文档类
14.1、相关类
- CDocument提供了一个用于管理数据的类,封装了关于数据的管理(数据提取,数据转换,数据存储等),并和视图类进行数据交互。
14.2、文档类的使用
- 定义一个自己的文档类(CMyDoc),派生自CDocument
- 利用框架类对象地址(pFrame)调用LoadFrame函数,创建框架 窗口。
- 在处理框架窗口的WM_CREATE消息时,动态创建视男科 类对象,并创建视图窗口。
- 在处理视图窗口的WM_CREATE消息时,将文档类对象和视图类对象建立关联关系。
在这里插入代码片
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
//文档类
class CMyDoc:public 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_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
afx_msg void OnPaint();
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyFrameWnd::OnPaint(){
PAINTSTRUCT ps={0};
HDC hdc=::BeginPaint(this->m_hWnd,&ps);
::TextOutA(hdc,100,100,"我是框架窗口",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_pNewViewClass=RUNTIME_CLASS(CMyView);
cct.m_pCurrentDoc=pDoc;
pFrame->LoadFrame(IDR_MENU,WS_OVERLAPPEDWINDOW,NULL,&cct);
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
14.3、对象关系图
-
theApp
theApp->m_pMainWnd
(保存pFrame框架类对象的地址)m_pViewActive
(保存pView视图类的对象地址,活动窗口视图)m_pDocument
(保存pDoc文档类对旬地址)m_viewList
(保存所有视图类对象地址)
-
文档类对象用一个链表成员变量,保存视图类对象地址
-
视图类对象用一个普通成员变量,保存文档类对象地址
-
因此,一个文档类对象可以对应多个视图类对象(视图窗口)
-
一个视图类对象(视图窗口),只能对应一个文档类对象
14.4、窗口切分
14.4.1、相关类
- CSplitterWnd- 不规则框架窗口类,封装了关于不规则框架窗口的操作
14.4.2、 窗口切分的使用
- 重写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)//动态创建机制
public:
virtual void OnDraw(CDC* pDC);//将文档类对象和视图类对象建立关联关系
};
IMPLEMENT_DYNCREATE(CMyView,CView)
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);
public:
CSplitterWnd split;//不规则框架窗口
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
ON_WM_PAINT()
END_MESSAGE_MAP()
void CMyFrameWnd::OnPaint(){
PAINTSTRUCT ps={0};
HDC hdc=::BeginPaint(this->m_hWnd,&ps);
::TextOutA(hdc,100,100,"我是框架窗口",strlen("我是框架窗口"));
::EndPaint(this->m_hWnd,&ps);
}
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
return CFrameWnd::OnCreate(pcs);//动态创建视图类对象,并创建视图窗口
}
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);//获取0行0列的视图窗口地址,并激活
return TRUE;
}
//应用程序类
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
CMyDoc* pDoc=new CMyDoc;
CCreateContext cct;
cct.m_pNewViewClass=RUNTIME_CLASS(CMyView);
cct.m_pCurrentDoc=pDoc;
pFrame->LoadFrame(IDR_MENU,WS_OVERLAPPEDWINDOW,NULL,&cct);
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
14.5、命令消息处理顺序
- 视图类–>文档类–>框架类–>应用程序类(默认顺序)。
14.6、文档类和视图类关系
-
theApp
theApp->m_pMainWnd
(保存pFrame框架类对象的地址)m_pViewActive
(保存pView视图类的对象地址,活动窗口视图)m_pDocument
(保存pDoc文档类对旬地址)m_viewList
(保存所有视图类对象地址)
-
视图类成员函数
获取和视图类对象关联的文档类对象,调用GetDocument()
-
文档类成员函数
当文档类数据发生变化时,调用UpDataAllViews
刷新和文档类相关联的视图类对象(视图窗口)。
#include <afxwin.h>
#include <afxext.h>
#include "resource.h"
//文档类
class CMyDoc:public CDocument{
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnNew();
public:
CString str;
};
BEGIN_MESSAGE_MAP(CMyDoc,CDocument)
ON_COMMAND(ID_NEW,OnNew)
END_MESSAGE_MAP()
void CMyDoc::OnNew(){
str="hello world";
//UpdateAllViews(NULL);//刷新和这个文档类对象关联的所有视图窗口
POSITION pos=this->GetFirstViewPosition();//GetFirstXXXPosition是迭代器
CView* pView=this->GetNextView(pos);//GetNextXXX,所有链表迭代都是这个套路
UpdateAllViews(pView);//刷新和这个文档类对象关联的除了pView这个视图窗口外所有视图窗口
}
//视图类
class CMyView:public CView{
DECLARE_DYNCREATE(CMyView)//动态创建机制
public:
virtual void OnDraw(CDC* pDC);//将文档类对象和视图类对象建立关联关系
};
IMPLEMENT_DYNCREATE(CMyView,CView)
void CMyView::OnDraw(CDC* pDC){
//CMyDoc* pDoc=(CMyDoc*)m_pDocument;
CMyDoc* pDoc=(CMyDoc*)GetDocument();//拿到的其实就是m_pDocument
pDC->TextOut(100,100,pDoc->str);
}
//框架类
class CMyFrameWnd:public CFrameWnd{
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT pcs);
virtual BOOL OnCreateClient(LPCREATESTRUCT pcs,CCreateContext* pContext);
public:
CSplitterWnd split;//不规则框架窗口
};
BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs){
return CFrameWnd::OnCreate(pcs);//动态创建视图类对象,并创建视图窗口
}
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);//获取0行0列的视图窗口地址,并激活
return TRUE;
}
//应用程序类
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
CMyDoc* pDoc=new CMyDoc;
CCreateContext cct;
cct.m_pNewViewClass=RUNTIME_CLASS(CMyView);
cct.m_pCurrentDoc=pDoc;
pFrame->LoadFrame(IDR_MENU,WS_OVERLAPPEDWINDOW,NULL,&cct);
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
15、单文档视图架构
15.1、单文档视图架构特点
- 只能管理一个文档,即只有一个文档类对象
- 视图没限制
15.2、单文档视图架构使用
- 参与架构的类
- CFrameWnd
- CWinApp
- CView
- CDocument
- 需要用到的类
- CDocTemplate(文档模板类)
- CSingleDocTemplate(单文档模板类)
- CDocManager 文档管理类
- CDocTemplate(文档模板类)
#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* pTemplte=new CSingleDocTemplate(IDR_MENU,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMyFrameWnd),
RUNTIME_CLASS(CMyView));
AddDocTemplate(pTemplte);
OnFileNew();
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
CMyWinApp theApp;
- 需要添加资源
15.3、对象关系图
- 参与架构的四个类除了应用程序类外,其余的三个类均支持动态创建机制。
- theApp
->m_pDocManager
(保存文档管理类对象地址)->m_templateList
(链表尾保存单文档模板类对象地址,即pTemplate)
CSingleDocTemplate* pTemplate
->m_pOnlyDoc
//(唯一的文档类对象地址)->m_pDocClass
//(RUNTIME_CLASS(CMyDoc))->m_pFrameClass
//(RUNTIME_CLASS(CMyFrameWnd))->m_pViewClass
//(RUNTIME_CLASS(CMyView))
16、多文档
16.1、多文档视图架构特点
- 可以管理多个文档(可以有多个文档类对象)。
- 多文档视图架构运行示意图如下:
16.1、多文档视图架构使用
- 参与架构的类
- CMDiFrameWnd 主框架类
- CMDIChildWnd 子框加类
- CWinApp
- CView
- CDocument
- 需要用到的类
- CDocTemplate(文档模板类)
- CMultiDocTemplate(多文档模板类)
- CDocManager 文档管理类
- CDocTemplate(文档模板类)
#include <afxwin.h>
#include "resource.h"
class CMyDoc:public CDocument{
DECLARE_DYNCREATE(CMyDoc)
};
IMPLEMENT_DYNCREATE(CMyDoc,CDocument)
class CMyView:public CView{
DECLARE_DYNCREATE(CMyView);
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{
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyFrameWnd* pFrame=new CMyFrameWnd;
pFrame->LoadFrame(IDR_MENU);
m_pMainWnd=pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
CMultiDocTemplate* pTemplate=new CMultiDocTemplate(IDR_MENU1,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMyChild),
RUNTIME_CLASS(CMyView));
AddDocTemplate(pTemplate);
OnFileNew();//创建文档类对象、子框架类对象、子框架窗口、视图类对象、视图窗口、文档类对象和视图类对象关联
OnFileNew();
OnFileNew();
return TRUE;
}
CMyWinApp theApp;
16.3、对象关系图
- theApp
->m_pDocManager
(保存文档管理类对象地址)->m_templateList
(链表尾保存单文档模板类对象地址,即pTemplate)
CMultiDocTemplate* pTemplate
->m_docList
//(保存多个文档类对象地址)->m_pDocClass
//(RUNTIME_CLASS(CMyDoc))->m_pFrameClass
//(RUNTIME_CLASS(CMyChild))->m_pViewClass
//(RUNTIME_CLASS(CMyView))
17、MFC绘图
Windows下的绘图,需要绘图设备
- Win32
绘图设备句柄(HDC) - MFC
类对象
17.1、相关类
- CDC类(绘图设备类)
封装了各种绘图相关的函数,以及两个非常重要的成员变量:m_hDC
和m_hAttribDc
。- CPaintDC类
封装了在WM_PAINT消息中绘图的绘图设备
窗口大小发生变化,产生WM_PAINT消息
在OnPaint函数中使用 - CClientDC类,
封装了在客户区绘图的绘图设备。
窗口大小发生变化,之前画的图就消失。
- CPaintDC类
- CGdiObject类(绘图对象类)
封装了各种绘图对象相关的操作,以及一个非常重要的成员变量m_hObject
(绘图对象句柄)。- CPen类,封装了画笔的操作
- CBrush类,封装了画刷的操作
- CFront类,封装了字体的操作
- CBitmap类,封装了位图的操作
17.2、绘图对象类的使用
17.2.1、画笔的使用CPen
/*画笔的使用*/
void CMFCDrawView::OnPen()
{
CClientDC dc(this);
CPen pen(PS_SOLID,2,RGB(255,0,0));
//MFC实现
//CPen* pOldPen=dc.SelectObject(&pen);
//dc.Rectangle(100,100,300,300);
//dc.SelectObject(pOldPen);
//pen.DeleteObject();
//Win32实现
HGDIOBJ nOldPen=::SelectObject(dc.m_hDC,pen.m_hObject);
::Rectangle(dc.m_hDC,100,100,300,300);
::SelectObject(dc.m_hDC,nOldPen);
::DeleteObject(pen.m_hObject);
}
17.2.2、画刷的使用CBrush
/*画刷的使用*/
void CMFCDrawView::OnBrush()
{
CClientDC dc(this);
CBrush brush(RGB(0,255,0));
//MFC实现
//CBrush* pOldBrush=dc.SelectObject(&brush);
//dc.Rectangle(100,100,300,300);
//dc.SelectObject(pOldBrush);
//brush.DeleteObject();
//Win32实现
HGDIOBJ nOldBrush=::SelectObject(dc.m_hDC,brush.m_hObject);
::Rectangle(dc.m_hDC,100,100,300,300);
::SelectObject(dc.m_hDC,nOldBrush);
::DeleteObject(brush.m_hObject);
}
17.2.3、字体的使用CFont
void CMFCDrawView::OnFont()
{
CClientDC dc(this);
CFont font;
font.CreatePointFont(300,"黑体");
CFont* pOldFont=dc.SelectObject(&font);
dc.TextOutA(100,100,"我是CFont");
dc.SelectObject(pOldFont);
font.DeleteObject();
}
17.2.4、位图的使用CBitmap
- 添加位图资源
- 创建一个和当前DC,相匹配的内存DC
- 将位图的数据送给内存DC
- 成像
- 将位图数据要回来
- 销毁位图
- 销毁内存DC
一、添加位图资源
void CMFCDrawView::OnBitmap()
{
//创建一个和当前DC,相匹配的内存DC
CClientDC dc(this);
CDC memdc;
memdc.CreateCompatibleDC(&dc);
// 将位图的数据送给内存DC
CBitmap bmp;
bmp.LoadBitmapA(IDB_BITMAP1);
CBitmap* pOldBitmap=memdc.SelectObject(&bmp);
//成像
dc.BitBlt(100,100,50,50,&memdc,0,0,SRCCOPY);
//将位图数据要回来
memdc.SelectObject(pOldBitmap);
//销毁位图
bmp.DeleteObject();
//销毁内存DC
memdc.DeleteDC();
}
18、序列化机制
- 读写硬盘文件。
18.1、文件操作相关类
- CFile文件操作类,封装了关于文件读写等操作
- CFile::Open
- CFile::Write
- CFile::Read
- CFile::Close
- CFile::SeekToBegin
- CFile::SeekToEnd
- CFile::Seek
#include <afxwin.h>
#include <iostream>
using namespace std;
void File(){
CFile file;
file.Open("file.txt",CFile::modeCreate|CFile::modeReadWrite);
char str[]="Hello file";
file.Write(str,strlen(str));
file.SeekToBegin();
char buf[256]={};
long nLen=file.Read(buf,255);
cout<<buf<<' '<<nLen<<endl;
file.Close();
}
int main(){
File();
return 0;
}
18.2、序列化基本类型
- 以二进制流形试读写硬盘文件。但效率很高。
18.2.1、序列化机制相关类
- CFile文件操作类,完成硬盘文件的读写操作。
- CArchive归档类,完成内存数据的读写操作。
18.2.2、序列化机制的使用
- 创建或打开文件
CFile::Open
- 定义归档类对象
CArchive ar;
- 数据序列化(存储/写)
ar<<数据
- 存储:序列化
- 读取:反序列化
- 关闭归档类对象
ar.Close()
- 关闭文件
CFile::Close()
18.2.3、反序列化机制的使用
- 打开文件
CFile::Open
- 定义归档类对象
CArchive ar;
- 数据序列化(加载/读)
ar>>数据
- 存储:序列化
- 读取:反序列化
- 关闭归档类对象
ar.Close()
- 关闭文件
CFile::Close()
18.2.4、序列化机制的代码
#include <afxwin.h>
#include <iostream>
using namespace std;
//序列化,写
void Store(){
CFile file;
file.Open("serial.txt",CFile::modeCreate|CFile::modeWrite);
CArchive ar(&file,CArchive::store,4096);//归档类对象,维护缓冲区。
long age=18;
ar<<age;
float score=88.5;
ar<<score;
CString name="abc";
ar<<name;
ar.Close();
file.Close();
}
//反序列化,读
void Load(){
CFile file;
file.Open("serial.txt",CFile::modeRead);
CArchive ar(&file,CArchive::load,4096);
long age;
ar>>age;
float score;
ar>>score;
CString name;
ar>>name;
ar.Close();
file.Close();
cout<<age<<' '<<score<<' '<<name<<endl;
}
int main(){
Store();
Load();
return 0;
}
18.2.5、序列化和反序列化执行过程
- CArchive类的部分数据成员
class CArchive{
enum Mode { store = 0, load = 1, bNoFlushOnDelete = 2, bNoByteSwap = 4 };
...
BOOL m_nMode; //访问方式
BOOL m_bUserBuf;
int m_nBufSize; //buff的大小
CFile* m_pFile; //操作的文件对象
BYTE* m_lpBufCur; //当前指向
BYTE* m_lpBufMax; //终止指向
BYTE* m_lpBufStart //开始指向
UINT m_nMapCount;
...
}
序列化执行过程:
- ar对象维护一个缓冲区
- 将各个数据依次序列化(存储)到ar对象维护的缓冲区中,并将m_lpBufCur的指针指向移动相应字节。
- 如果ar维扩的缓冲区不足,则将ar维护的缓冲区的数据写入硬盘文件,并重置m_lpBufCur为开始指向。
- 当关闭ar对象时,将ar对象维护的缓冲区数据写入硬盘文件,并释放ar对象维护的缓冲区。
反序列化执行过程:与序列化执行过程相反。
18.3、序列化类对象
18.3.1、序列化类对象的使用
- 类必须派生自CObject
- 类必须添加声明宏
DECLARE_SERIAL(theClass)
- 类外必须添加实现宏
IMPLEMENT_SERIAL(theClass,baseClass,1)
- 类必须重写虚函数Serialize
- 注:IMPLEMENT_SERIAL(theClass,baseClass,1)中第三个参数为版本,随便写。
当类具备上述要件后,类对象就可以序列化到文件中保存。
#include <afxwin.h>
#include <iostream>
using namespace std;
class CMyDoc:public CDocument{
DECLARE_SERIAL(CMyDoc)
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)
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("serial.txt",CFile::modeCreate|CFile::modeWrite);
CArchive ar(&file,CArchive::store,4096);
CMyDoc data(18,88.5,"abc");
ar<<&data; //序列化对象,就是将对象的各个成员变量序列化
ar.Close();
file.Close();
}
//反序列化,读
void Load(){
CFile file;
file.Open("serial.txt",CFile::modeRead);
CArchive ar(&file,CArchive::load,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;
}
19、Win32对话框回顾
19.1、间接方式创建无模式对话框
- 添加对话框资源
- 查找资源FindResource
- 加载资源 LoadResource
- 锁定资源 LockResource
- 创建无模式对话框CreateDialogIndirect
19.2、直接方式创建无模式对话框
- CreateDialog
19.3、其它
- 将资源ID转换成字符形式,MAKEINTRESOURCE(ID_MENU1)
- 销毁无模式对话框,使用DestroyWindow ,不能使用EndDialog,因为 EndDialog只能隐藏无模式对话框,不能销毁。
- EndDialog可以销毁模式对话框
20、对话框架构
20.1、对话框分类
- 模式对话框(假)
MFC中的对话框均是无模式对话框。当弹出对话框,让主窗口禁用(EnableWindow),以实现模式对话框的效果。 - 无模式对话框
本进程其它窗口操作不受影响。
20.2、无模式对话框
20.2.1、参与架构的类
- CDialog
- CWinApp
20.2.2、无模式对话框的使用
- 添加对话框资源
- 定义一个自己的对话框类(CMyDlg),管理对话框资源,派生自CDialog或CDialogEx均可。
#include <afxwin.h>
#include "resource.h"
class CMyDlg:public CDialog{
DECLARE_MESSAGE_MAP()
public:
void OnOK();
void OnCancel();
};
BEGIN_MESSAGE_MAP(CMyDlg,CDialog)
ON_COMMAND(IDOK,OnOK)
ON_COMMAND(IDCANCEL,OnCancel)
END_MESSAGE_MAP()
void CMyDlg::OnOK(){
//CDialog::OnOK();//只将无模式对话框隐藏,并未销毁
::DestroyWindow(this->m_hWnd);
}
void CMyDlg::OnCancel(){
//CDialog::OnCancle();
this->DestroyWindow();//类的成员函数
}
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;
20.2.3、无模式对话框的执行过程
- 程序启动进入入口函数WinMain
- 获取应用程序类对象theApp的地址
- 利用theApp地址调用InitApplication,初始化当前应用程序的数据
- 利用theApp地址调用InitInstance函数,在函数中创建无模式对话框并显示
- 利用theApp地址调用CWinApp的Run函数进行消息循环
如果没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理
当对话框销毁(必须利用DestroyWindow),消息循环才可以退出。
退出前利用theApp地址调用ExitInstance虚函数实现退出前的善后处理工作。
20.3、模式对话框(假)
20.3.1 、参与架构的类
- CDialog
- CWinApp
20.3.2、模式对话框的使用
- 添加对话框资源
- 定义一个自己的对话框类(CMyDlg),管理对话框资源,派生自CDialog或CDialogEx均可。
#include <afxwin.h>
#include "resource.h"
class CMyDlg:public CDialog{
DECLARE_MESSAGE_MAP()
public:
enum{IDD=IDD_DIALOG1};
CMyDlg():CDialog(IDD){
}
afx_msg void OnOK();
};
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;
dlg.DoModal();
return FALSE;//不再执行MFC库中安排的Run函数
}
CMyWinApp theApp;
- 程序启动进入入口函数WinMain
- 获取应用程序类对象theApp的地址
- 利用theApp地址调用InitApplication,初始化当前应用程序的数据
- 利用theApp地址调用InitInstance函数,在函数中调用DoModal函数
- DoModal内部执行过程:
- 将父窗口设置为不可用
- 创建无模式对话框
- 进入消息循环(自带的)
- 退出消息循环(父类的OnOK/OnCancel导致循环退出)
- 将父窗口设置为可用状态
- 销毁无模式对话框
- 不再执行CWinApp的Run函数进行的消息循环
- 程序结束。
21、对象和控件绑定
21.1、绑定的作用
- 将控件窗口和类对象绑定具有两大作用
- 如果和数据类对象绑定,对象和控件可以进行数据交换
- 如果和控件类对象绑定,对象就可以代表整个控件
21.2、与数据类型对象绑定
- 数据类型对象和控件可实现数据交互
- 重写父类成员函数DoDataExchange(程序启动会先执行一次该函数)
在函数内部通过一系列的DDX_xxx函数,实现控件和数据类型对象的数据交互 - 如果需要实现数据交互,调用UpdateData函数
- UpdateData(TRUE):控件 —>变量(会调用DoDataExchange)
- UpdateData(FALSE):变量 —>控件(会调用DoDataExchange)
#include <afxwin.h>
#include "resource.h"
class CMyDlg:public CDialog{
DECLARE_MESSAGE_MAP()
public:
enum{IDD=IDD_DIALOG1};
CMyDlg():CDialog(IDD){
}
afx_msg void OnBtn1();
afx_msg void OnBtn2();
public:
CString m_strEdit;//数据类型对象
virtual void DoDataExchange(CDataExchange* pDX);
};
BEGIN_MESSAGE_MAP(CMyDlg,CDialog)
ON_COMMAND(IDC_BUTTON1,OnBtn1)
ON_COMMAND(IDC_BUTTON2,OnBtn2)
END_MESSAGE_MAP()
void CMyDlg::OnBtn1(){//控件--》数据对象
UpdateData(TRUE);
AfxMessageBox(m_strEdit);
}
void CMyDlg::OnBtn2(){//数据对象--》控件
m_strEdit="Hello world!";
UpdateData(FALSE);
}
void CMyDlg::DoDataExchange(CDataExchange* pDX){
DDX_Text(pDX,IDC_EDIT1,m_strEdit);//绑定
}
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyDlg dlg;//栈空间
m_pMainWnd=&dlg;
dlg.DoModal();
return FALSE;//不再执行MFC库中安排的Run函数
}
CMyWinApp theApp;
21.3、与控件类型对象绑定
- 控件类型对象和控件可实现对象代表整个控件
- 重写父类成员函数DoDataExchange(程序启动会先执行一次该函数)
在函数内部通过一系列的DDX_xxx函数,实现控件句柄和控件类型对象的绑定。 - 控件类型对象,就代表这个控件。
#include <afxwin.h>
#include "resource.h"
class CMyDlg:public CDialog{
DECLARE_MESSAGE_MAP()
public:
enum{IDD=IDD_DIALOG1};
CMyDlg():CDialog(IDD){
}
afx_msg void OnBtn1();
afx_msg void OnBtn2();
afx_msg void OnBtn3();
public:
CString m_strEdit;//数据类型对象
CEdit m_ctrEdit;//控件类对象
virtual void DoDataExchange(CDataExchange* pDX);
};
BEGIN_MESSAGE_MAP(CMyDlg,CDialog)
ON_COMMAND(IDC_BUTTON1,OnBtn1)
ON_COMMAND(IDC_BUTTON2,OnBtn2)
ON_COMMAND(IDC_BUTTON3,OnBtn3)
END_MESSAGE_MAP()
void CMyDlg::OnBtn1(){//控件--》数据对象
UpdateData(TRUE);
AfxMessageBox(m_strEdit);
}
void CMyDlg::OnBtn2(){//数据对象--》控件
m_strEdit="Hello world!";
UpdateData(FALSE);
}
void CMyDlg::OnBtn3(){
m_ctrEdit.MoveWindow(0,0,200,200);
}
void CMyDlg::DoDataExchange(CDataExchange* pDX){
DDX_Text(pDX,IDC_EDIT1,m_strEdit);//绑定数据对象,调用一系列DDX_xxx函数,类向导可实现自动添加
DDX_Control(pDX,IDC_EDIT1,m_ctrEdit);//绑定控件对象
}
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyDlg dlg;//栈空间
m_pMainWnd=&dlg;
dlg.DoModal();
return FALSE;//不再执行MFC库中安排的Run函数
}
CMyWinApp theApp;
22、控件消息的处理
-
控件消息的处理
- WM_COMMAND消息
- LOWORD(wParam) 菜单ID、控件ID
- HIWORD(wParam) 菜单项 为0;控件:通知码(控件发生的事情,比如单击BN_CLICKED、双击)
- lParam 均无用
注:用
ON_WM_COMMAND
宏处理WM_COMMAND消息,只能处理按钮的鼠标单击消息。 用ON_BN_CLICKED
宏处理是最佳选择。 - WM_COMMAND消息
#include <afxwin.h>
#include "resource.h"
class CMyDlg:public CDialog{
DECLARE_MESSAGE_MAP()
public:
enum{IDD=IDD_DIALOG1};
CMyDlg():CDialog(IDD){
}
afx_msg void OnBtn1();
afx_msg void OnBtn2();
afx_msg void OnBtn3();
afx_msg void OnDblClk();
public:
CString m_strEdit;//数据类型对象
CEdit m_ctrEdit;//控件类对象
virtual void DoDataExchange(CDataExchange* pDX);
};
BEGIN_MESSAGE_MAP(CMyDlg,CDialog)
ON_BN_CLICKED(IDC_BUTTON1,OnBtn1)
ON_BN_CLICKED(IDC_BUTTON2,OnBtn2)
ON_BN_DOUBLECLICKED(IDC_BUTTON2,OnDblClk)//处理按钮双击消息
ON_COMMAND(IDC_BUTTON3,OnBtn3)//ON_COMMAND宏只可以处理按钮的单击消息,最好用ON_BN_CLICKED宏
END_MESSAGE_MAP()
void CMyDlg::OnBtn1(){//控件--》数据对象
UpdateData(TRUE);
AfxMessageBox(m_strEdit);
}
void CMyDlg::OnBtn2(){//数据对象--》控件
m_strEdit="Hello world!";
UpdateData(FALSE);
}
void CMyDlg::OnBtn3(){
m_ctrEdit.MoveWindow(0,0,200,200);
}
void CMyDlg::OnDblClk(){
AfxMessageBox("被双击了");
}
void CMyDlg::DoDataExchange(CDataExchange* pDX){
DDX_Text(pDX,IDC_EDIT1,m_strEdit);//绑定数据对象,调用一系列DDX_xxx函数,类向导可实现自动添加
DDX_Control(pDX,IDC_EDIT1,m_ctrEdit);//绑定控件对象
}
class CMyWinApp:public CWinApp{
public:
virtual BOOL InitInstance();
};
BOOL CMyWinApp::InitInstance(){
CMyDlg dlg;//栈空间
m_pMainWnd=&dlg;
dlg.DoModal();
return FALSE;//不再执行MFC库中安排的Run函数
}
CMyWinApp theApp;
要想按钮可以接收鼠标双击消息,需将Notify=True
23、基本控件
- 控件如何与数据类型对象绑定(做数据交换)
- 控件如何与控件类对象绑定(对象代表控件)
- 控件的消息如何处理
23.1、下压式按钮
- 相关类
CButton类:父类为CWnd,封装了关于下压式按钮控件的各种操作。 - 消息
当下压式按钮被单击
WM_COMMAND消息(通知码:BN_CLICKED)
一、对象绑定
头文件中声明CButton m_btn;
DoDataExchange函数中增加DDX_Control(pDX, IDC_BUTTON1, m_btn);
23.2、编辑框
- 相关类
CEdit类:父类为CWnd,封装了关于编辑框控件的各种操作。 - 常见操作
获取编辑框文本内容GetWindowText
设置编辑框文本内容SetWindowText - 消息
当编辑框文本内容发生变化
WM_COMMAND消息(通知码EN_CHANGE)
23.2.1、控件类对象绑定
一、打开类向导
二、成员变量标签页,双击需要对象绑定的控件ID
三、自定义一个成员变量,点击【确定】
四、添加完成员变量后点击【确定】
五、至此控件绑定对象成功,类向导自动写入的代码如下
- 头文件中添加了
CEdit m_edit;
- 源文件
DoDataExchange
函数中新增了DDX_Control(pDX, IDC_EDIT1, m_edit);
23.2.2、数据类型对象绑定
一、进入类向导,选择控件ID,点击【添加变量】
二、类别选择Value
三、成员变量标签页会多一条记录,点击【确定】
四、创建成功,类向导会自动写入以下代码
- 头文件
CString m_str;
- 源文件DoDataExchange函数中新增
DDX_Text(pDX, IDC_EDIT1, m_str);
23.3、复选框按钮
-
相关类
CButton类:父类为CWnd,封装了关于复选框按钮控件的各种操作。 -
常见操作
获取复选框状态:CButton::GetCheck
设置复选框状态:CButton::SetCheck -
消息
当下压式按钮被单击
WM_COMMAND消息(通知码BN_CLICKED) -
其它属性
设置复选框按钮属性push like=True
,外观将变成和下压式按钮一样。
也称多态按钮。
23.4、单选框按钮
- 相关类
CButton类:父类为CWnd,封装了关于单选框按钮控件的各种操作。 - 常见操作
获取复选框状态:CButton::GetCheck
设置复选框状态:CButton::SetCheck - 消息
当下压式按钮被单击
WM_COMMAND消息(通知码BN_CLICKED) - 其它属性
须先设置单选框按钮的属性Group=True,才能在类向导,成员变量标签页找到该控件ID。
23.5、分组框按钮
- 常见操作
用于控件的分组显示,提高界面友好性 。 - 分组框一般不绑定,如果要绑定可以和CButton类绑定
- 修改了分组框ID以后,就能在类向导,成员变量标签页找到该控件ID。
23.6、文本静态框Static text
- 常见操作
常用于显示字符串,提高界面友好性
CWnd::SetWindowText - 对象绑定
CStatic - 修改了分组框ID以后,就能在类向导,成员变量标签页找到该控件ID。
23.7、图像静态框
- 常见操作
常用于显示图像
CStatic::SetIcon 设置ICO图片
CStatic::SetBitMap 设置bmp图片,如
m_ps.SetBitmap(::LoadBitmapA(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDB_BITMAP2)));
- 对象绑定
CStatic
修改了分组框ID以后,就能在类向导,成员变量标签页找到该控件ID。 - 其它属性
设置属性Type=Icon
设置属性Type=Bitmap
设置Image=位图的资源ID
23.8、组合框Combo Box
23.8.1、组合框分类
- 设置控件的Type属性可得到
- 简单式组合框:支持文本输入
- 下拉式组合框:支持文本输入
- 下拉列表式组合框:不支持文本输入
23.8.2、组合框一些属性
- 设置控件的属性sort=True,可对添加到列表框的字符串进行自动排序。
23.8.2、组合框使用
- 相关类
CComboBox类:父类为CWnd,封装了关于组合框控件的各种操作。 - 常见操作
添加选项,CComboBox::AddString
删除某个选项,CComboBox::DeleteString
删除全部选项,CComboBox::ResetContent
获取选择项索引,CComboBox::GetCurSel
设置选择项,CComboBox::SetCurSel
获取选项文本内容,CComboBox::GetLBText
设置选项附加数据,CComboBox::SetItemData
获取选项附加数据,CComboBox::GetItemData - 消息
当选择项发生变化时
WM_COMMAND消息,(通知码CBN_SELCHANGE)
当文本内容发生变化时
WM_COMMAND消息(通知码CBN_EDITCHANGE)
23.9、列表框List Box
- 相关类
CListBox类:父类为CWnd,封装了关于列表框控件的各种操作。 - 常见操作
添加选项,CListBox::AddString
删除选项,CListBox::DeleteString
获取选择项,CListBox::GetCurSel
设置选择项,CListBox::SetCurSel
获取选项文本内容,CListBox::GetItemText
设置选项附加数据,CListBox::SetItemData
获取选项附加数据,CListBox::GetItemData
23.10、滑块控件Slider Control
- 控件重要属性
Orientation=Horizontal 水平控件
Orientation=Vertical 垂直控件 - 相关类
CSliderCtrl类:父类为CWnd,封装了关于滑块控件的各种操作。 - 常见操作
设置范围,CSliderCtrl::SetRange
设置位置,CSliderCtrl::SetPos
获取位置,CSliderCtrl::GetPos - 消息
当滑块位置改变后
滑块控件发出WM_COMMAND消息(通知码NM_CUSTOMDRAW)
23.11、进度条控件Progress Control
- 相关类
CProgressCtrl类:父类为CWnd,封装了关天进度条控件的各种操作。 - 常见操作
设置范围,CProgressCtrl::SetRange
设置位置,CProgressCtrl::SetPos
获取位置,CProgressCtrl::GetPos
23.12、列表控件List Control
23.12.1、列表控件的基本使用
- 相关类
CListCtrl类,父类为CWnd,封装了关于列表控件的各种操作。 - 风格,设置控件的View属性
- 图标
LVS_ICON
,设置控件的属性View=Icon - 小图标
LVS_SMALLICON
,设置控件的属性View=Small Icon - 列表
LVS_LIST
,设置控件的属性View=List - 报表
LVS_REPORT
,设置控件的属性View=Report
- 图标
- 修改风格
CWnd::ModifyStyle - 设置经纬线(报表样式可以设置)
SetExtendedStyle
例如:
m_list.ModifyStyle(LVS_SMALLICON|LVS_LIST|LVS_REPORT,LVS_ICON);
m_list.SetExtendedStyle(LVS_EX_GRIDLINES);//经纬线
- 常见操作
- 添加选项CListCtrl::InsertItem
- 删除选项CListCtrl::DeleteItem
- 删除全部选项CListCtrl::DeleteAllItem
- 添加列CListCtrl::InsertColumn
- 设置文本内容CListCtrl::SetItemText
- 设置图像CListCtrl::SetImageList
- 设置附加数据CListCtrl::SetItemData
- 获取附加数据CListCtrl::GetItemData
- 消息
- 当列表控件被双击
- 发出WM_COMMAND消息(通知码NM_DBLCLK)
23.12.2、列表控件的图标使用
一、创建一个48*24的位图
二、部分代码如下
m_list.InsertColumn(0,"名称",LVCFMT_LEFT,150);
m_list.InsertColumn(0,"修改日期",LVCFMT_LEFT,150);
m_list.InsertColumn(0,"类型",LVCFMT_LEFT,150);
m_list.InsertColumn(0,"大小",LVCFMT_LEFT,150);
m_list.SetExtendedStyle(LVS_EX_GRIDLINES);//经纬线
CImageList* pImgLst=new CImageList;
pImgLst->Create(IDB_BITMAP1,24,1,RGB(255,255,255));//将IDB_BITMAP1,按宽度24像像分割成一个图标,屏蔽RGB(255,255,255)色
m_list.SetImageList(pImgLst,LVSIL_NORMAL);//将图像链表应用在列表控件的图标风格中
m_list.SetImageList(pImgLst,LVSIL_SMALL);//将图像链表应用在列表控件的其它风格中
m_list.InsertItem(0,"目录",0);
m_list.SetItemText(0,1,"2001.01.01");
m_list.SetItemText(0,2,"Dir");
m_list.SetItemText(0,3,"3M");
m_list.InsertItem(1,"文件",1);
展示效果如下。
23.12.3、列表控件展示目录中的文件
//OnInitDialog函数中写入
ShowFile("C:");
void CMFCListDlg::ShowFile(CString path){
CString strPath=path;
strPath+="/*.*";
CFileFind find;
BOOL goFind=find.FindFile(strPath);//开启查找
int i=0;
while(goFind){
goFind=find.FindNextFile();//找到当前文件,将文件信息保存到find对象中
CString filename=find.GetFileName();
CString filepath=find.GetFilePath();
if(find.IsDirectory()&&!find.IsDots()){
m_list.InsertItem(i,filename,0);
}
else
m_list.InsertItem(i,filename,1);
i++;
}
find.Close();
}
23.12.2、列表控件实现双击目录展示其内文件的效果
void CMFCListDlg::ShowFile(CString path){
m_list.DeleteAllItems();
CString strPath=path;
strPath+="/*.*";
CFileFind find;
BOOL goFind=find.FindFile(strPath);//开启查找
int i=0;
while(goFind){
goFind=find.FindNextFile();//找到当前文件,将文件信息保存到find对象中
CString filename=find.GetFileName();
//CString filepath=find.GetFilePath();
CString* filepath=new CString;//内存分配到了堆上
*filepath=find.GetFilePath();
if(find.IsDirectory()){
m_list.InsertItem(i,filename,0);
m_list.SetItemData(i,(DWORD)filepath);
}
else
m_list.InsertItem(i,filename,1);
i++;
}
find.Close();
}
void CMFCListDlg::OnDblclkList(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
*pResult = 0;
DWORD nData=m_list.GetItemData(pNMItemActivate->iItem);//双击的选项
CString* filepath=(CString*)nData;
ShowFile(*filepath);
}
23.13、树控件 Tree Control
23.13.1、树控件 基本使用
- 相关类
CTreeCtrl类:父类为CWnd,封装了关于树控件的各种操作。 - 常见操作
- 添加选项CTreeCtrl::InsertItem
- 删除选项CTreeCtrl::DeleteItem
- 删除所有选项CTreeCtrl::DeleteAllItems
- 获取选择项CTreeCtrl::GetSelectedItem
- 获取选项文本内容CTreeCtrl::GetItemText
- 设置选项高度CTreeCtrl::SetItemHeight
- 设置图像CTreeCtrl::SetImageList
23.13.2、树控件一些重要属性
- Has Buttons=True,父项旁边显示加号(+)和减号(-)按钮。
- Has Lines=True,显示层次结构的虚线。
- Lines At Root=True,根结点也显示层次结构的虚线。
23.13.3、树控件添加图标
一、添加一个48*12的位图资源
二、关键代码
CImageList* pImgLst=new CImageList;
pImgLst->Create(IDB_BITMAP1,12,1,RGB(255,255,255));
m_tree.SetImageList(pImgLst,LVSIL_NORMAL);
HTREEITEM nRoot=m_tree.InsertItem("Object",0,1,NULL);//第二个参数:未选中显示的图标;第三个参数:选中显示的图标
m_tree.InsertItem("CCmdTarget",0,0,nRoot);
m_tree.InsertItem("CWnd",0,0,nRoot);
m_tree.SetItemHeight(30);
三、展示效果
24、文件搜索类
24.1、文件搜索类的使用
- 相关类
CFileFind类:封装了关于文件遍历的各种操作。 - 常见操作
- 开启查找,成功返回TRUE,失败返回FALSE。
CFileFind::FindeFile - 找到当前文件,返回下一个文件是否存在。
CFileFind::FindNextFile - 调用一系列GetXXX函数获取文件信息。
CFileFind::GetFileName
CFileFind::GetFilePath - 调用一系列ISXXX函数判断文件属性
- CFileFind::IsDirectory 判断是不是目录
- CFileFind::IsDotS 判断是不是.或..
- 关闭查找
CFileFind::Close
- 开启查找,成功返回TRUE,失败返回FALSE。
24.2、遍历一个文件夹下的文件
void Find(CString path){
CString strPath=path;
strPath+="/*.*";
CFileFind find;
BOOL goFind=find.FindFile(strPath);//开启查找
while(goFind){
goFind=find.FindNextFile();//找到当前文件,将文件信息保存到find对象中
CString filename=find.GetFileName();
if(find.IsDirectory())
cout<<"["<<filename<<"]"<<endl;
else
cout<<filename<<endl;
}
find.Close();
}
24.3、循环遍历一个文件夹下的所有文件
using namespace std;
void Find(CString path){
CString strPath=path;
strPath+="/*.*";
CFileFind find;
BOOL goFind=find.FindFile(strPath);//开启查找
while(goFind){
goFind=find.FindNextFile();//找到当前文件,将文件信息保存到find对象中
CString filename=find.GetFileName();
CString filepath=find.GetFilePath();
if(find.IsDirectory()&&!find.IsDots()){
cout<<"["<<filename<<"]"<<endl;
Find(filepath);
}
else
cout<<filename<<endl;
}
find.Close();
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
Find("H:");
return 0;
}