MFC深入浅出

文章目录


1、MFC类库常用头文件

1.1、afx.h

afx.h 各种MFC头文件包含在内。

1.2、afxwin.h

afxwin.h 包含了各种MFC窗口类。包含了afx.hwindows.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 文档管理类
#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 文档管理类
#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_hDCm_hAttribDc
    • CPaintDC类
      封装了在WM_PAINT消息中绘图的绘图设备
      窗口大小发生变化,产生WM_PAINT消息
      在OnPaint函数中使用
    • CClientDC类,
      封装了在客户区绘图的绘图设备。
      窗口大小发生变化,之前画的图就消失。
  • 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宏处理是最佳选择。

#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)));
  1. 对象绑定
    CStatic
    修改了分组框ID以后,就能在类向导,成员变量标签页找到该控件ID。
  2. 其它属性
    设置属性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

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;
}
  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值