1、MFC有一个位图按钮类,即为CBitmapButton,能够实现简单的按钮贴图美化;但是长久没有使用,今天就遇到一些问题:主要就是CBitmapButton::AutoLoad这个函数的使用,总是出错,无法正确的加载图片;现在将需要注意的问题贴出来,以作备份。


2、首先,说明以下不用AutoLoad的初始化方法,我在VS2010下测试成功,完全可以用类向导添加一个CBitmapButton型的控件变量(即在DoDataExchange关联控件与变量)而不用手动调用SubclassDlgItem函数。只需要将预先导入的位图加载到按钮类变量中就行了,具体如下:

(1)在对话框上放置一个按钮,修改其自绘(Owner Draw)属性为TRUE;

(2)利用类向导为该按钮关联一个控件变量m_ctlBtn;

(3)在按钮控件所在的对话框类的OnInitDialog函数中添加加载图片的代码:

m_ctlBtn.LoadBitmaps(IDB_UP, IDB_DOWN, IDB_FOCUS, IDB_DISABLE);
m_ctlBtn.SizeToContent(); // 根据加载的图片,调整按钮大小

(4)关于LoadBitmaps函数,只有第一个参数(即按钮正常状态下的位图资源ID)是必须的,其它参数可以不设:



BOOL LoadBitmaps( UINT nIDBitmapResource, UINT nIDBitmapResourceSel = 0, UINT nIDBitmapResourceFocus = 0, UINT nIDBitmapResourceDisabled = 0 );

(5)OK,这样就行了


3、现在在说用AutoLoad函数加载图片,以及相应的注意点:

(1)关于AutoLoad函数,可以先看一下它的实现代码:


// Autoload will load the bitmap resources based on the text of
//  the button
// Using suffices "U", "D", "F" and "X" for up/down/focus/disabled
BOOL CBitmapButton::AutoLoad(UINT nID, CWnd* pParent)
{
	// first attach the CBitmapButton to the dialog control
	if (!SubclassDlgItem(nID, pParent))
		return FALSE;

	CString buttonName;
	GetWindowText(buttonName);
	ASSERT(!buttonName.IsEmpty());      // must provide a title

	LoadBitmaps(buttonName + _T("U"), buttonName + _T("D"),
	  buttonName + _T("F"), buttonName + _T("X"));

	// we need at least the primary
	if (m_bitmap.m_hObject == NULL)
		return FALSE;

	// size to content
	SizeToContent();
	return TRUE;
}


(2)可以看到,在AutoLoad函数中先调用SubclassDlgItem关联控件,所以我们就不能使用类向导关联控件了,具体来说就是在对话框类的DoDataExchange函数中的DDX_Control(... ...)会与此处的SubclassDlgItem冲突,如果一定要用类向导,只要将响应的DDX_Control语句删除就行了;

(3)接着继续看AutoLoad的实现代码,也就是获取按钮文字(Caption),然后调用LoadBitmaps函数加载图片;

即LoadBitmaps的参数是根据按钮文字加上一个字母来确定的;

(4)按钮的文字,即为属性窗口中的Caption内容;按照这样说来,是不是只要将资源对话框中相应位图的ID改为Caption中的内容加上一个字母就行了呢?答案是不行的。很多人都会犯这个错误,我一开始也犯了这个错误,纠结了好一会。

(5)原因是AutoLoad函数中调用的LoadBitmaps函数并不是前面提到的那个以位图ID为参数的函数,仔细想一想,Caption中设置的是一个字符串,然后在这个字符串后面加一个字母,依然是一个字符串;而控件的ID其实只是一个整数。显然AutoLoad中的LoadBitmaps有一个重载函数:


BOOL LoadBitmaps( LPCTSTR lpszBitmapResource, LPCTSTR lpszBitmapResourceSel = NULL, LPCTSTR lpszBitmapResourceFocus = NULL, LPCTSTR lpszBitmapResourceDisabled = NULL );



(6)接下来看一下这个函数的实现:


// LoadBitmaps will load in one, two, three or all four bitmaps
// returns TRUE if all specified images are loaded
BOOL CBitmapButton::LoadBitmaps(LPCTSTR lpszBitmapResource,
	LPCTSTR lpszBitmapResourceSel, LPCTSTR lpszBitmapResourceFocus,
	LPCTSTR lpszBitmapResourceDisabled)
{
	// delete old bitmaps (if present)
	m_bitmap.DeleteObject();
	m_bitmapSel.DeleteObject();
	m_bitmapFocus.DeleteObject();
	m_bitmapDisabled.DeleteObject();

	if (!m_bitmap.LoadBitmap(lpszBitmapResource))
	{
		TRACE(traceAppMsg, 0, "Failed to load bitmap for normal image.\n");
		return FALSE;   // need this one image
	}
	BOOL bAllLoaded = TRUE;
	if (lpszBitmapResourceSel != NULL)
	{
		if (!m_bitmapSel.LoadBitmap(lpszBitmapResourceSel))
		{
			TRACE(traceAppMsg, 0, "Failed to load bitmap for selected image.\n");
			bAllLoaded = FALSE;
		}
	}
	if (lpszBitmapResourceFocus != NULL)
	{
		if (!m_bitmapFocus.LoadBitmap(lpszBitmapResourceFocus))
			bAllLoaded = FALSE;
	}
	if (lpszBitmapResourceDisabled != NULL)
	{
		if (!m_bitmapDisabled.LoadBitmap(lpszBitmapResourceDisabled))
			bAllLoaded = FALSE;
	}
	return bAllLoaded;
}



(7)可以看到最终调用的是CBitmap类的成员函数LoadBitmap加载图片的,但是CBitmap类下的该函数也有两个重载:



BOOL LoadBitmap ( LPCTSTR lpszResourceName ); BOOL LoadBitmap ( UINT nIDResource );



(8)到此问题是清楚了,但是关于以LPCTSTR 型数据为参数LoadBitmap函数实际上参数并不是实际的文件路径,许多人可能都看到过,说CBitmap::LoadBitmap不能直接加载硬盘上的文件,譬如这样是不行的(虽然代码并不报错):


CBitmap oBitmap;

oBitmap.LoadBitmap(_T("D://test.bmp"));


(9)实际上这里的参数依然是类似ID号的一种资源序号,只不过以字符串的形式来表示,但是却又不是简单的从ID号转化而来的,因为本文已经扯得比较远,而且这个问题事实上还有许多内容,本文就不再深入下去了;下面回归主题,述说如何使用AutoLoad加载图片。


4、使用AutoLoad加载图片的步骤:

(1)同样,在对话框上添加一个按钮,更改其自绘属性;

(2)手动添加一个CBitmapButton型的成员变量m_bmpBtn;

(3)关键步骤:位图资源、按钮ID 和 按钮Caption的命名,命名规则如下:

按钮ID: IDC_BTN_TEST_

按钮Caption: BTN_TETST_

位图ID(注意,包含分号): "TEST_U"、"TEST_D"、"TEST_F"、"TEST_X"


5、注意点:

(1)按钮Caption是根据按钮ID设置的:

a、若按钮ID中有字符”_“,则以按钮ID中第一个”_“字符后面的所有字符串为按钮Caption;

b、若按钮ID中无字符”_“,则以ID名为按钮Caption。


eg1:若按钮ID为:IDC_BUTTON_UP     则按钮Caption为:BUTTON_UP

eg2:若按钮ID为:IDC_BTN_DOWN 则按钮Caption为:BTN_DOWN

eg3:若按钮ID为:BTN_FOCUS 则按钮Caption为:FOCUS

eg4:若按钮ID为:DISABLE 则按钮Caption为:DISABLE


(2)位图资源ID是根据按钮Caption设置的:

a、根据位图所表示的按钮状态:UP、DOWN、FOCUS和DISABLE,分别在按钮Caption后面加上一个大写字母U或D或F或X;

b、然后用双引号将这串字符引起来;


eg1:若按钮Caption为:BUTTON_UP 

则按钮位图资源ID为:"BUTTON_UPU""BUTTON_UPD""BUTTON_UPF""BUTTON_UPX"
eg2:若按钮Caption为:BTN_DOWN
则按钮位图资源ID为:"BTN_DOWNU""BTN_DOWND""BTN_DOWNF""BTN_DOWNX"

eg3:若按钮Caption为:FOCUS
则按钮位图资源ID为:"FOCUSU""FOCUSD""FOCUSF""FOCUSX"