您的位置:首页 > 编程语言 > C语言/C++

MFC学习笔记(4)——Windows程序的类封装 下

2016-12-10 09:54 281 查看
无论从功能上还是代码隔离的角度来看,用于消息处理的窗口函数应该封装成一个独立的类。这个类提供了消息处理能力,它是所有消息去向的目的地,所以将这个类命名为CCmdTarget,意为命令目标类。

1.窗口函数的简单封装

 窗口函数WndProc()是一个全局函数,不能直接把它封装到CCmdTarget类中,只能把它留在类外,把它的函数体封装到CCmdTarget类的成员函数中,如,AfxWndProc()。这样,既把原窗口的功能封装到了类中,又保留了系统与该功能之间的联系通道。AfxWndProc()与原窗口函数WndProc()的关系及事件消息的流动方式如下图:



把消息处理功能封装到一个单独的CCmdTarget类中还有另一个优点,凡是希望具有消息处理能力的类都可以自CCmdTarget来派生。这样一来,在整个类体系中就又多了一个类CCmdTarget,而一个Windows程序的结构也变成下图所示:



其引入CCmdTarget类的代码如下:

//需要包含的头文件-------------------------------------------------------------------------
#include "stdafx.h"
#include "afxwinappex.h"
#include "afxdialogex.h"
#include "MFCTest.h"
#include<Windows.h>
//定义全局变量和函数------------------------------------------------------------------------
HINSTANCE hInstance;
HINSTANCE hInst;
MSG msg;
char lpszClassName[] = "window_class";
char* ShowText;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//窗口函数
//CCmdTarget------------------------------------------------------------------------------
class CCmdTarget1
{
public:
HWND hWnd;
int WndProc1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
virtual void OnLButtonDown();
void OnPaint(HDC hdc);
virtual void OnDestroy();
};
int CCmdTarget1::WndProc1(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
OnLButtonDown();
break;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(hWnd, &ps);
OnPaint(hdc);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
OnDestroy();
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

void CCmdTarget1::OnLButtonDown()
{
}
void CCmdTarget1::OnPaint(HDC hdc)
{
TextOut(hdc, 50, 50, ShowText, 16);
}
void CCmdTarget1::OnDestroy()
{
PostQuitMessage(0);
}

//窗口类-------------------------------------------------------------------------------------
class CFrameWnd1:public CCmdTarget1
{
public:
int RegisterWindow();
void Create(LPCTSTR lpClassName, LPCTSTR lpWindowName);
void ShowWindow(int nCmdShow);
void UpdateWindow();
};
//窗口类成员函数-------------------------------------------------------------------------------------
int CFrameWnd1::RegisterWindow()
{
WNDCLASS wc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.style = 0;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = lpszClassName;
wc.lpszMenuName = NULL;
return RegisterClass(&wc);
}

void CFrameWnd1::Create(LPCTSTR lpClassName, LPCTSTR lpWindowName)
{
RegisterWindow();
hInst = hInstance;
hWnd = CreateWindow(lpszClassName, lpWindowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
}

void CFrameWnd1::ShowWindow(int nCmdShow)
{
::ShowWindow(hWnd, nCmdShow);
}

void CFrameWnd1::UpdateWindow()
{
::UpdateWindow(hWnd);
}

//应用程序类的-------------------------------------------------------------------------------------
class CWinApp1
{
public:
CWinApp1*m_pCurrentWinApp;
public:
CWinApp1();
~CWinApp1();
public:
CFrameWnd1*m_pMainWnd;
public:
virtual BOOL InitInstance(int nCmdShow);//声明为虚函数
int Run();
};

CWinApp1::CWinApp1()
{
m_pCurrentWinApp = this;
}

BOOL CWinApp1::InitInstance(int nCmdShow)
{
m_pMainWnd = new CFrameWnd1;
m_pMainWnd->Create(NULL, "封装的Windows程序");
m_pMainWnd->ShowWindow(nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}

int CWinApp1::Run()
{
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

CWinApp1::~CWinApp1()
{
delete m_pMainWnd;
}

//程序员派生的窗口类-------------------------------------------------------------------------------------
class CMyWnd :public CFrameWnd1
{
void OnLButtonDown();
};
void CMyWnd::OnLButtonDown()
{
ShowText = "新定义的消息处理。";
InvalidateRect(hWnd, NULL, 1);
}
//由CWinApp1类派生CMyApp类--------------------------------------------------------------------------------
class CMyApp:public CWinApp1
{
public:
BOOL InitInstance(int nCmdShow); //重新定义InitInstance函数
};
//重新定义的成员函数InitInstance--------------------------------------------------------------------------
BOOL CMyApp::InitInstance(int nCmdShow)
{
CMyWnd*pMainWnd;
pMainWnd = new CMyWnd; //应用窗体的派生类定义窗体对象
pMainWnd->Create(NULL, "用新InitInstance函数的程序");
pMainWnd->ShowWindow(nCmdShow);
pMainWnd->UpdateWindow();
m_pMainWnd = pMainWnd; //把CMyWnd类的对象赋给m_pMainWnd
return TRUE;
}
//程序员定义的CWinApp的对象MyApp--------------------------------------------------------------------------
CMyApp MyApp;
//全局函数AfxGetApp()-------------------------------------------------------------------------------------
CWinApp1*AfxGetApp1()
{
return MyApp.m_pCurrentWinApp;
}
//主函数-------------------------------------------------------------------------------------
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
int ResultCode = -1;
CWinApp1* pApp;
pApp = AfxGetApp1();
pApp->InitInstance(nCmdShow);
return ResultCode = pApp->Run();
}
//窗口函数-------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,WPARAM wParam, LPARAM lParam)
{
CWinApp1*pApp = AfxGetApp1();
return pApp->m_pMainWnd->WndProc1(hWnd, message, wParam, lParam);
}
//------------------------------------------
其运行结果如下图:





2.消息映射

a.消息处理函数

MFC要求在声明具有消息处理能力的类时,除了该类要以CCmdTarget类或其派生类为基类之外,还要求对每一个需要响应的消息都要在其类中声明如下形式的消息处理函数。

afx_msg void 消息处理函数名();其中,afx_msg是表示消息处理函数的关键字。
如,某个类希望可以处理WM_LBUTTONDOWN、WM_PAINT和WM_DESTROY这三个消息,那么在该类中就应该声明并实现如下三个消息处理函数:

afx_msg void OnLButtonDown();
afx_msg void OnDraw();
afx_msg void OnDestroy();b.类的消息映射表
在MFC中,消息映射表项的结构为AFX_MSGMAP_ENTRY,其源码如下:

struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};如果类中含有消息处理函数,则需要在类中声明一个AFX_MSGMAP_ENTRY类型的数组 _messageEntries[]来作为消息映射表。
c.消息映射总表

由于用于检索消息映射表的for循环只是在CCmdTarget类中,因此为了使这个循环能检索那些散落在各个类中的消息映射表,MFC使用了链表结构。MFC要求在每一个具有消息映射表的类中都设置了一个具有两个指针的链表节点。其中叫做lpEntries的指针用来指向本类消息映射表_messageEntries;而另一个叫做pBaseMap的指针用来指向基类的链表节点,于是通过这些分别挂接了各自类消息映射表的节点,就把那些零散的消息映射表链成一个大表。这个大表就叫做“消息映射总表”或“消息映射表”。

消息映射总表如下图,图中A类为基类,它派生了B类,而B类又派生了C类,下图中虚线框中的即为单向链表的各个节点,他们各自挂接本类的类消息映射表。



下面贴一个表示了在有继承关系的两个类中依靠消息映射表寻找消息处理函数的工作过程。下图中虚线表示的是B类对象接受消息,且消息对应的消息处理函数中B的基类A类消息映射表的情况;而实线表示的是B类对象接收到消息,而消息对应的消息处理函数就在B类消息映射表的情况。



3.消息映射表的声明和实现

由上面叙述,在设计一个需要响应消息的类时,必须在类中声明一个消息映射表,而且要在类外实现该表。MFC把完成上述功能的代码封装在若干个宏中。这些宏不仅要把系统中各种类型的消息及其处理函数指针尽可能规范统一地填写到类消息映射表的各个表项中,而且还要负责建立链表的节点,把类的消息映射表链接到总表中。

a.宏DECLARE_MESSAGE_MAP

本宏使用在类的声明中,用来声明消息映射表。

b.宏BEGIN_MESSAGE_MAP

本宏使用在类声明之外,用来定义链表节点和填写链表节点中的数据,其格式为:   BEGIN_MESSAGE_MAP(类名称,基类名称)

c.ON_XXXXX

本宏使用在宏BEGIN_MESSAGE_MAP()的后面,依次填写类消息映射表中的各个表项。

MFC把消息主要分为三大类:标准消息、命令消息和Notification消息。

标准消息的消息标识形式为:WM_XXXX,它的消息映射形式为:ON_WM_XXXX,而对应的消息处理函数名称为:ONXXX

如:  ON_WM_LBUTTONDOWN() 这种宏没有参数,系统会自动把该消息的标识WM_LBUTTONDOWN和对应的消息处理函数名为afx_msg OnLButtonDown添入类消息映射表的相应位置。



命令消息WM_COMMAND是来自菜单、工具条按钮、加速键等用户接口对象的WM_COMMAND通知消息,属于应用程序自己定义的消息,其一般格式为:

ON_COMMAND(<消息标识>,<对应的消息处理函数>)  

如:

ON_COMMAND(IDM_FILENEW,OnFileNew)

ON_COMMAND(IDM_FILEOPEN,OnFileOpen)

Notification消息是由按钮、文本编辑框等控件产生的消息,以下列举几种:

对于按钮控件(Button)的单击事件消息映射的实现宏为: ON_BN_CLICKED(<消息标识>,<对应的消息处理函数>)

对于组合框(ComboBox)的双击事件消息映射的实现宏为:ON_CBN_DBLCLK(<消息标识>,<对应的消息处理函数>)

对于文本编辑框控件(Edit)的双击事件消息映射的实现宏为:ON_EN_DBLCLK(<消息标识>,<对应的消息处理函数>)

d.END_MESSAGE_MAP

消息处理函数表的结束宏,是消息映射表结束的标志。

以下为一含有消息映射的Windows应用程序:

//需要包含的头文件-------------------------------------------------------------------------
#include "stdafx.h"
#include "afxwinappex.h"
#include "afxdialogex.h"
#include "MFCTest.h"
#include<afxwin.h>
//由CFrameWnd派生的CMyWnd类------------------------------------------------------
class CMyWnd :public CFrameWnd
{
private:
char* ShowText; //声明一个字符串为数据成员
public:
afx_msg void OnPaint(); //声明WM_PAINT消息处理函数
afx_msg void OnLButtonDown(UINT nFlags,CPoint point);//声明鼠标左键按下消息处理函数
DECLARE_MESSAGE_MAP(); //声明消息映射
};
//消息映射的实现----------------------------------------------------------------------
BEGIN_MESSAGE_MAP(CMyWnd,CFrameWnd)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
//WM_PAINT消息处理函数的实现------------------------------
void CMyWnd::OnPaint()
{
CPaintDC dc(this);
dc.TextOut(20, 20, ShowText);
}
//WM_LBUTTONDOWMT消息处理函数的实现--------------------------------------
void CMyWnd::OnLButtonDown(UINT nFlags,CPoint point)
{
ShowText = "有消息映射表的程序";
InvalidateRect(NULL, TRUE);
}
//程序员由CWinApp派生的应用程序类---------------------------------------------
class CMyApp :public CWinApp
{
public:
BOOL InitInstance();
};
BOOL CMyApp::InitInstance()
{
CMyWnd*pMainWnd = new CMyWnd;
pMainWnd->Create(0, "MFC");
pMainWnd->ShowWindow(m_nCmdShow);
pMainWnd->UpdateWindow();
m_pMainWnd = pMainWnd;
return TRUE;
}
//定义CMyApp的对象MyApp----------------------------------------------------------
CMyApp MyApp;
//---------------------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息