CDialogEx的一个解剖

用MFC的都知道我们经常使用的对话框基本上都是继承于CDialogEx这个类的,看着MFC包装的如此神秘,今天是这把那些不认识的给他翻译出来然后看看这个类的真面目。

首先我先附上CDialogEx的源代码一份。

如下是CDialogEx.h的内容:


#include "afxcontrolbarutil.h"
#include "afxdialogimpl.h"

#ifdef _AFX_PACKING
#pragma pack(push, _AFX_PACKING)
#endif

#ifdef _AFX_MINREBUILD
#pragma component(minrebuild, off)
#endif

/*============================================================================*/
// CDialogEx dialog

class CDialogEx : public CDialog
{
	friend class CMFCPopupMenu;
	friend class CMFCDropDownListBox;
	friend class CContextMenuManager;

	DECLARE_DYNAMIC(CDialogEx)

// Construction
public:
	CDialogEx();
	CDialogEx(UINT nIDTemplate, CWnd *pParent = NULL);
	CDialogEx(LPCTSTR lpszTemplateName, CWnd *pParentWnd = NULL);

protected:
	void CommonConstruct();

// Attributes:
public:
	enum BackgroundLocation
	{
		BACKGR_TILE,
		BACKGR_TOPLEFT,
		BACKGR_TOPRIGHT,
		BACKGR_BOTTOMLEFT,
		BACKGR_BOTTOMRIGHT,
	};

protected:
	HBITMAP            m_hBkgrBitmap;
	CSize              m_sizeBkgrBitmap;
	CBrush             m_brBkgr;
	BackgroundLocation m_BkgrLocation;
	CDialogImpl     m_Impl;
	BOOL               m_bAutoDestroyBmp;

// Operations:
public:
	void SetBackgroundColor(COLORREF color, BOOL bRepaint = TRUE);
	void SetBackgroundImage(HBITMAP hBitmap, BackgroundLocation location = BACKGR_TILE, BOOL bAutoDestroy = TRUE, BOOL bRepaint = TRUE);
	BOOL SetBackgroundImage(UINT uiBmpResId, BackgroundLocation location = BACKGR_TILE, BOOL bRepaint = TRUE);

// Overrides
public:
	virtual BOOL PreTranslateMessage(MSG* pMsg);

protected:
	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

// Implementation
protected:
	afx_msg void OnActivate(UINT nState, CWnd *pWndOther, BOOL bMinimized);
	afx_msg BOOL OnNcActivate(BOOL bActive);
	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
	afx_msg void OnDestroy();
	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
	afx_msg void OnSysColorChange();
	afx_msg void OnSettingChange(UINT uFlags, LPCTSTR lpszSection);

	DECLARE_MESSAGE_MAP()

	void SetActiveMenu(CMFCPopupMenu* pMenu);
};

#ifdef _AFX_MINREBUILD
#pragma component(minrebuild, on)
#endif

#ifdef _AFX_PACKING
#pragma pack(pop)
#endif

如下是CDialogEx.cpp的内容:

#include "stdafx.h"

#include "afxdialogex.h"
#include "afxpopupmenu.h"
#include "afxtoolbarmenubutton.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

IMPLEMENT_DYNAMIC(CDialogEx, CDialog)

/
// CDialogEx dialog

#pragma warning(disable : 4355)

CDialogEx::CDialogEx() : m_Impl(*this)
{
	CommonConstruct();
}

CDialogEx::CDialogEx(UINT nIDTemplate, CWnd *pParent/*= NULL*/) : CDialog(nIDTemplate, pParent), m_Impl(*this)
{
	CommonConstruct();
}

CDialogEx::CDialogEx(LPCTSTR lpszTemplateName, CWnd *pParentWnd/*= NULL*/) : CDialog(lpszTemplateName, pParentWnd), m_Impl(*this)
{
	CommonConstruct();
}

#pragma warning(default : 4355)

void CDialogEx::CommonConstruct()
{
	m_hBkgrBitmap = NULL;
	m_sizeBkgrBitmap = CSize(0, 0);
	m_BkgrLocation = (BackgroundLocation) -1;
	m_bAutoDestroyBmp = FALSE;
}

void CDialogEx::SetBackgroundColor(COLORREF color, BOOL bRepaint)
{
	if (m_brBkgr.GetSafeHandle() != NULL)
	{
		m_brBkgr.DeleteObject();
	}

	if (color != (COLORREF)-1)
	{
		m_brBkgr.CreateSolidBrush(color);
	}

	if (bRepaint && GetSafeHwnd() != NULL)
	{
		Invalidate();
		UpdateWindow();
	}
}

void CDialogEx::SetBackgroundImage(HBITMAP hBitmap, BackgroundLocation location, BOOL bAutoDestroy, BOOL bRepaint)
{
	if (m_bAutoDestroyBmp && m_hBkgrBitmap != NULL)
	{
		::DeleteObject(m_hBkgrBitmap);
	}

	m_hBkgrBitmap = hBitmap;
	m_BkgrLocation = location;
	m_bAutoDestroyBmp = bAutoDestroy;

	if (hBitmap != NULL)
	{
		BITMAP bmp;
		::GetObject(hBitmap, sizeof(BITMAP), (LPVOID) &bmp);

		m_sizeBkgrBitmap = CSize(bmp.bmWidth, bmp.bmHeight);
	}
	else
	{
		m_sizeBkgrBitmap = CSize(0, 0);
	}

	if (bRepaint && GetSafeHwnd() != NULL)
	{
		Invalidate();
		UpdateWindow();
	}
}

BOOL CDialogEx::SetBackgroundImage(UINT uiBmpResId, BackgroundLocation location, BOOL bRepaint)
{
	HBITMAP hBitmap = NULL;

	if (uiBmpResId != 0)
	{
		hBitmap = ::LoadBitmapW(AfxFindResourceHandle(MAKEINTRESOURCE(uiBmpResId), RT_BITMAP),
			MAKEINTRESOURCEW(uiBmpResId));
		if (hBitmap == NULL)
		{
			ASSERT(FALSE);
			return FALSE;
		}
	}

	SetBackgroundImage(hBitmap, location, TRUE /* Autodestroy */, bRepaint);
	return TRUE;
}

BEGIN_MESSAGE_MAP(CDialogEx, CDialog)
	ON_WM_ACTIVATE()
	ON_WM_NCACTIVATE()
	ON_WM_ERASEBKGND()
	ON_WM_DESTROY()
	ON_WM_CTLCOLOR()
	ON_WM_SYSCOLORCHANGE()
	ON_WM_SETTINGCHANGE()
END_MESSAGE_MAP()

/
// CDialogEx message handlers

void CDialogEx::OnActivate(UINT nState, CWnd *pWndOther, BOOL /*bMinimized*/)
{
	Default();
	m_Impl.OnActivate(nState, pWndOther);
}

BOOL CDialogEx::OnNcActivate(BOOL bActive)
{
	m_Impl.OnNcActivate(bActive);

	// Do not call the base class because it will call Default()
	// and we may have changed bActive.
	return(BOOL) DefWindowProc(WM_NCACTIVATE, bActive, 0L);
}

BOOL CDialogEx::OnEraseBkgnd(CDC* pDC)
{
	if (m_brBkgr.GetSafeHandle() == NULL && m_hBkgrBitmap == NULL)
	{
		return CDialog::OnEraseBkgnd(pDC);
	}

	ASSERT_VALID(pDC);

	CRect rectClient;
	GetClientRect(rectClient);

	if (m_BkgrLocation != BACKGR_TILE || m_hBkgrBitmap == NULL)
	{
		if (m_brBkgr.GetSafeHandle() != NULL)
		{
			pDC->FillRect(rectClient, &m_brBkgr);
		}
		else
		{
			CDialog::OnEraseBkgnd(pDC);
		}
	}

	if (m_hBkgrBitmap == NULL)
	{
		return TRUE;
	}

	ASSERT(m_sizeBkgrBitmap != CSize(0, 0));

	if (m_BkgrLocation != BACKGR_TILE)
	{
		CPoint ptImage = rectClient.TopLeft();

		switch (m_BkgrLocation)
		{
		case BACKGR_TOPLEFT:
			break;

		case BACKGR_TOPRIGHT:
			ptImage.x = rectClient.right - m_sizeBkgrBitmap.cx;
			break;

		case BACKGR_BOTTOMLEFT:
			ptImage.y = rectClient.bottom - m_sizeBkgrBitmap.cy;
			break;

		case BACKGR_BOTTOMRIGHT:
			ptImage.x = rectClient.right - m_sizeBkgrBitmap.cx;
			ptImage.y = rectClient.bottom - m_sizeBkgrBitmap.cy;
			break;
		}

		pDC->DrawState(ptImage, m_sizeBkgrBitmap, m_hBkgrBitmap, DSS_NORMAL);
	}
	else
	{
		// Tile background image:
		for (int x = rectClient.left; x < rectClient.Width(); x += m_sizeBkgrBitmap.cx)
		{
			for (int y = rectClient.top; y < rectClient.Height(); y += m_sizeBkgrBitmap.cy)
			{
				pDC->DrawState(CPoint(x, y), m_sizeBkgrBitmap, m_hBkgrBitmap, DSS_NORMAL);
			}
		}
	}

	return TRUE;
}

void CDialogEx::OnDestroy()
{
	if (m_bAutoDestroyBmp && m_hBkgrBitmap != NULL)
	{
		::DeleteObject(m_hBkgrBitmap);
		m_hBkgrBitmap = NULL;
	}

	m_Impl.OnDestroy();

	CDialog::OnDestroy();
}

HBRUSH CDialogEx::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	if (m_brBkgr.GetSafeHandle() != NULL || m_hBkgrBitmap != NULL)
	{
#define AFX_MAX_CLASS_NAME 255
#define AFX_STATIC_CLASS _T("Static")
#define AFX_BUTTON_CLASS _T("Button")

		if (nCtlColor == CTLCOLOR_STATIC)
		{
			TCHAR lpszClassName [AFX_MAX_CLASS_NAME + 1];

			::GetClassName(pWnd->GetSafeHwnd(), lpszClassName, AFX_MAX_CLASS_NAME);
			CString strClass = lpszClassName;

			if (strClass == AFX_BUTTON_CLASS || strClass == AFX_STATIC_CLASS)
			{
				pDC->SetBkMode(TRANSPARENT);

				if (m_brBkgr.GetSafeHandle() != NULL && IsAppThemed())
				{
					return (HBRUSH)m_brBkgr.GetSafeHandle();
				}
				else
				{
					return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
				}
			}
		}
	}

	return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}

BOOL CDialogEx::PreTranslateMessage(MSG* pMsg)
{
	if (m_Impl.PreTranslateMessage(pMsg))
	{
		return TRUE;
	}

	return CDialog::PreTranslateMessage(pMsg);
}

void CDialogEx::SetActiveMenu(CMFCPopupMenu* pMenu)
{
	m_Impl.SetActiveMenu(pMenu);
}

BOOL CDialogEx::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if (m_Impl.OnCommand(wParam, lParam))
	{
		return TRUE;
	}

	return CDialog::OnCommand(wParam, lParam);
}

void CDialogEx::OnSysColorChange()
{
	CDialog::OnSysColorChange();

	if (AfxGetMainWnd() == this)
	{
		GetGlobalData()->UpdateSysColors();
	}
}

void CDialogEx::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
	CDialog::OnSettingChange(uFlags, lpszSection);

	if (AfxGetMainWnd() == this)
	{
		GetGlobalData()->OnSettingChange();
	}
}

我们认真的分析一下CDialogEx的公用方法,可以看到相对于基类Dialog,CDialogEx多了几个设置窗口背景颜色和背景图片等功能。

那么我们新手们看表面的都应该容易懂,但是如下的内容是不是有点云里雾里的。
 

//CDialogEx.h中
DECLARE_DYNAMIC(CDialogEx)

DECLARE_MESSAGE_MAP()


//CDialogEx.cpp中
IMPLEMENT_DYNAMIC(CDialogEx, CDialog)

BEGIN_MESSAGE_MAP(CDialogEx, CDialog)
	ON_WM_ACTIVATE()
	ON_WM_NCACTIVATE()
	ON_WM_ERASEBKGND()
	ON_WM_DESTROY()
	ON_WM_CTLCOLOR()
	ON_WM_SYSCOLORCHANGE()
	ON_WM_SETTINGCHANGE()
END_MESSAGE_MAP()

我是在VS中查看定义,然后进行关键翻译。

1、 首先来看看  DECLARE_DYNAMIC(CDialogEx),查看定义我们会找到以下代码

#define DECLARE_DYNAMIC(class_name) \
public: \
	static const CRuntimeClass class##class_name; \
	virtual CRuntimeClass* GetRuntimeClass() const; \

在#define 中 \的意思是与下内容相连,但后面的字符必须是回车。

还有一个  class##class_name的语法,可以理解为两个相连。比如,当前class_name 内容是CDialogEx,那么class##class_name的结果就是  classCDialogEx,这是一个代码名,不是字符串。

如此,我们可以将DECLARE_DYNAMIC(CDialogEx)可以替换为如下代码:

public:
    static const CRuntimeClass  classCDialogEx;
	virtual CRuntimeClass*GetRuntimeClass() const;

会发现,不就弄了一个静态变量的动态类(运行时类),还有一个获取运行时类的函数,不过在头文件中,这里是声明的部分,相信.cpp文件中肯定还有定义。

2、DECLARE_MESSAGE_MAP()

        查看定义,会发现有如下代码

#define DECLARE_MESSAGE_MAP() \
protected: \
	static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
	virtual const AFX_MSGMAP* GetMessageMap() const; \

//其中AFX_MSGMAP是一个消息回调相关的2个结构体
//如下是这2个结构体的内容
struct AFX_MSGMAP
{
	const AFX_MSGMAP* (PASCAL * pfnGetBaseMap)();
	const AFX_MSGMAP_ENTRY* lpEntries;
};

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)
};


//PASCAL  的定义
#define PASCAL  __stdcall 

都把定义查到了,DECLARE_MESSAGE_MAP()可以替换为:

protected: 
	static const AFX_MSGMAP* __stdcall GetThisMessageMap(); 
       //AFX_MSGMAP是一个结构体
	virtual const AFX_MSGMAP* GetMessageMap() const;

3、IMPLEMENT_DYNAMIC(CDialogEx, CDialog)

查看定义,会发现有如下代码

#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
	IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \
	AFX_COMDAT const CRuntimeClass class_name::class##class_name = { \
		#class_name, sizeof(class class_name), wSchema, pfnNew, \
			RUNTIME_CLASS(base_class_name), NULL, class_init }; \
	CRuntimeClass* class_name::GetRuntimeClass() const \
		{ return RUNTIME_CLASS(class_name); }

//AFX_COMDAT 的定义
#ifndef AFX_COMDAT
#define AFX_COMDAT __declspec(selectany)
#endif

这里会发现,尽然嵌套了两层定义

首先要把IMPLEMENT_DYNAMIC(CDialogEx, CDialog)替换为:

IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

然后我们再来看IMPLEMENT_RUNTIMECLASS的内容。IMPLEMENT_RUNTIMECLASS中有带了5个参数,后面三个大家都看得到,前面两个传入的参数其实是CDialogEx,  CDialog;知道了参数,那么我们就可以再次替换为如下代码:

// 带入参数为 class_name = CDialogEx    base_class_name = CDialog
//	wSchema:0xFFFF     pfnNew:NULL    class_init:  NULL
//   IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL, NULL)

	__declspec(selectany) const CRuntimeClass CDialogEx::classCDialogEx=
	{
		"CDialogEx", 
        sizeof(class CDialogEx),
        0xFFFF, 
        NULL,
		RUNTIME_CLASS(CDialog), 
        NULL, 
        NULL
	}

	CRuntimeClass* CDialogEx::GetRuntimeClass() const	
	{
		 return RUNTIME_CLASS(CDialogEx); 
	}

 4、BEGIN_MESSAGE_MAP(CDialogEx, CDialog)
           ON_WM_PAINT()
       END_MESSAGE_MAP()

这些是消息映射的地方,只拿ON_WM_PAINT()来做示范,其他的我们自己动手看看也是可以的,关键看到BEGIN~~~~到 END这2个东东的内在乾坤就好。

BEGIN_MESSAGE_MAP(CBaseDialog, CDialogEx)
	
END_MESSAGE_MAP()


//这2个其实是一个函数的上一半和下一半。
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
	PTM_WARNING_DISABLE \
	const AFX_MSGMAP* theClass::GetMessageMap() const \
		{ return GetThisMessageMap(); } \
	const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
	{ \
		typedef theClass ThisClass;						   \
		typedef baseClass TheBaseClass;					   \
		__pragma(warning(push))							   \
		__pragma(warning(disable: 4640))                    \
         /* message maps can only be called by single threaded message pump */ \
		static const AFX_MSGMAP_ENTRY _messageEntries[] =  \
		{


#define ON_WM_PAINT() \
	{ WM_PAINT, 0, 0, 0, AfxSig_vv, \
		(AFX_PMSG)(AFX_PMSGW) \
		(static_cast< void (AFX_MSG_CALL CWnd::*)(void) > ( &ThisClass :: OnPaint)) },




#define END_MESSAGE_MAP() \
		{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
	}; \
		__pragma(warning(pop))	\
		static const AFX_MSGMAP messageMap = \
		{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
		return &messageMap; \
	}								  \
	PTM_WARNING_RESTORE


//其中的另外两个看不懂的定义如下:
#define PTM_WARNING_DISABLE \
	__pragma(warning( push )) \
	__pragma(warning( disable : 4867 ))


#define PTM_WARNING_RESTORE \
	__pragma(warning( pop ))


//另外几个定义
#define WM_PAINT                        0x000F
#define AFX_MSG_CALL
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);

按照我们找出的定义,可以翻译为:

//翻译后:带入参数值  theClass:CDialogEx     baseClass:CDialog
//---------------------------------------------------------------
//BEGIN_MESSAGE_MAP(CBaseDialog, CDialogEx)  的内容
    __pragma(warning( push )) 
	__pragma(warning( disable : 4867 ))

	const AFX_MSGMAP* CDialogEx::GetMessageMap() const 
	{
		 return GetThisMessageMap(); 
	} 
	const AFX_MSGMAP* __stdcall CDialogEx::GetThisMessageMap() 
	{ 
		typedef CDialogEx ThisClass;						
		typedef CDialog TheBaseClass;			
		__pragma(warning(push))							
		__pragma(warning(disable: 4640)) /* message maps can only be called by single threaded message pump */
 
		static const AFX_MSGMAP_ENTRY _messageEntries[] =  
		{

//----------------------------------------------------------------------
// ON_WM_PAINT()的内容
        { 
            WM_PAINT, 0, 0, 0, AfxSig_vv,
        	(AFX_PMSG)(AFX_PMSGW)(static_cast<void( CWnd::*)(void) > ( &ThisClass :: OnPaint)) 
        },


//----------------------------------------------------------------------
//END_MESSAGE_MAP()的内容

        { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } 
		}; 
		__pragma(warning(pop))

		static const AFX_MSGMAP messageMap = 
		{ 
			&CDialog::GetThisMessageMap, &_messageEntries[0] 
		}; 
		return &messageMap; 
	}
	__pragma(warning( pop ))


以上就是对CDialogEx类的一个解剖,如果大家有兴趣想自己动手写一个类,不妨就不要用MFC云山雾罩的写法,就把原模原样的写出来,这样我们会对Dialog类有了一层新的认识。

这篇只是本人作为MFC初学者的一篇学术研究型文章,如果有不足之处,请指正。

另外通过这一层剖析后,我就会急着写一个自己的Dialog类,基类为CDialogEx,然后也可以实现类向导添加其他消息函数和虚函数等。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值