MFC分析工具条和状态条的创建的分析

在MFC中,工具条和状态条作为主窗口的子窗口在ON_WM_CREATE消息中创建。工具条通过CreateWindowEx创建,并发送TB_ADDBUTTONS消息添加按钮信息,涉及TBBUTTON结构体数组。状态条则更简单,主要管理区域划分。注意区分CLASSNAME和WINDOWNAME,以及正确设置窗口属性。MFC提供了LoadToolBar来加载工具条资源,但这需要对PE文件有基本理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工具条和状态条的在comctl32.lib当中实现,要想得到TOOLBAR和STATUS的支持,程序必须添加CommCtrl头文件,并且在初始化TOOLBAR和STATUSBAR之前,调用函数InitCommonControls函数对整个库进行初始化。

工具条和状态条都属于主窗口的子窗口,所以创建的时机是在主窗口的ON_WM_CREATE消息当中。第一步,需要调用相应的函数创建出窗口句柄出来,这个函数就是CreateWindowEx。

HWND hToolBar = CreateWindowEx(0,
		TOOLBARCLASSNAME,
		NULL,
		WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS |
		TBSTYLE_FLAT | CCS_BOTTOM | TBSTYLE_TOOLTIPS,
		0,
		40,
		340,
		30,
		hwnd,
		NULL,
		hInst,
		NULL);

其中TOOLBARCLASSNAME在CommCtrl头文件中被定义,

#define TOOLBARCLASSNAMEW       L"ToolbarWindow32"
#define TOOLBARCLASSNAMEA       "ToolbarWindow32"
#ifdef  UNICODE
#define TOOLBARCLASSNAME        TOOLBARCLASSNAMEW
#else
#define TOOLBARCLASSNAME        TOOLBARCLASSNAMEA
#endif

接下来是向刚刚创建的工具条发送TB_ADDBUTTONS消息,这个消息是由系统处理的,用于向系统添加TOOLBAR的按钮信息。这个按钮信息由TBBUTTON结构体数组组成,

typedef struct _TBBUTTON {
    int iBitmap;			//图片在IMAGELIST当中的索引号
    int idCommand;		//与这个工具条项目相对应的空间ID号
    BYTE fsState;		//工具条项目的状态,使能或者禁止
  BYTE fsStyle;		//工具条项目的风格,也就是工具条项目的表现形式,
  BYTE bReserved[2];//保留用于对齐          
    DWORD_PTR dwData; //用于存放与工具条项目相关的数据地址
    INT_PTR iString;  //用于存放与工具条项目相关的字符串的地址,
} TBBUTTON

其中iBitMap和dwData、iString互斥使用,也就是说这个只能是三个当中的一个有效。

由于当使用iBitmap的时候,需要一个HIMAGELIST管理TBBUTTON相关的图片,其中iBitmap是HIMAGELIST的索引,所以首先需要创建一个HIMAGELIST。调用相应的宏ImageList_Create就可以了,调用之后就利用ImageList_AddMasked或者ImageList_Add向HIMAGELIST添加图片。

	HIMAGELIST hImageList;
	hImageList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR24, 1, 1);
	if (!hImageList)
	{
		MessageBox(hwnd, _T("ImageList it is not created!"), NULL, MB_OK);
		return;
	}
	ImageList_AddMasked(hImageList,
		(HBITMAP)LoadImage(hInst, MAKEINTRESOURCE(IDB_PLAYICON), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR), RGB(255, 255, 255));
	ImageList_Destroy((HIMAGELIST)SendMessage(hToolBar,
		TB_SETIMAGELIST,
		0,
		(LPARAM)hImageList));

接下来发送TB_ADDBUTTONS消息就可以了。

SendMessage(hToolBar, TB_ADDBUTTONS, NumButtons, (LPARAM)Buttons);

相对而言STATUSBAR就简单多了,因为STATUSBAR仅仅管理一系列的空闲区间,所以STATUSBAR最主要的功能就是将自身的区间给划分成一定的区间。

hwndStatus=CreateWindowEx(0,STATUSCLASSNAME,TEXT(""),SBS_SIZEGRIP|WS_CHILD | WS_VISIBLE |WS_CLIPSIBLINGS, 0,0,0,0,hwnd,NULL,hInst,NULL);
	RECT rcClient;  
	int width[3];                       
	GetClientRect(hwnd, &rcClient);       
	int length=rcClient.right/3;          
	width[0]=length;                    
	width[1]=length*2;                  
	width[2]=length*3;  
	SendMessage(hwndStatus, SB_SETPARTS, 3, (LPARAM)(LPINT)width);  
	MoveWindow(hwndStatus,0,0,0,0,TRUE);
	SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)TEXT("就绪"));  
	TCHAR status_line[MAX_PATH],status_size[MAX_PATH];
	wsprintf(status_line,TEXT("%d,%d"),1,1);  
	wsprintf(status_size,TEXT("%d"),0);  
	SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)1, (LPARAM)status_line);  
	SendMessage(hwndStatus, SB_SETTEXT, (WPARAM)2, (LPARAM)status_size);

 以上代码摘自网络,创建STATUSBAR和TOOLBAR的时候要注意,第一,分清楚CLASSNAME和WINDOWNAME;第二,创建的时候窗口的属性设置是否正确。

由于MFC是对WIN32编程的封装,所以处理流程和WIN32一样。最大的变化就是,程序员可以直接创建一个TOOLBAR工具条资源,然后利用LoadToolBar函数将资源一次性加载到程序当中,不过这一部分的理解需要对PE文件有一定概念。

struct CToolBarData
{
	WORD wVersion;
	WORD wWidth;
	WORD wHeight;
	WORD wItemCount;

	WORD* items()
		{ return (WORD*)(this+1); }
};
BOOL CToolBar::LoadToolBar(LPCTSTR lpszResourceName)
{
	HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_TOOLBAR);//搜索到工具条所在的句柄,其中RT_TOOLBAR是PE文件当中的标志ID,而PE文件加载到内存不会引起PE文件内部内部内容的改变
	HRSRC hRsrc = ::FindResource(hInst, lpszResourceName, RT_TOOLBAR);//类似于树形结构,从主目录下面搜索相应的子目录
	if (hRsrc == NULL)
		return FALSE;

	HGLOBAL hGlobal = LoadResource(hInst, hRsrc);//
	if (hGlobal == NULL)
		return FALSE;

	CToolBarData* pData = (CToolBarData*)LockResource(hGlobal);
	if (pData == NULL)
		return FALSE;
	UINT* pItems = new UINT[pData->wItemCount];
	for (int i = 0; i < pData->wItemCount; i++)
		pItems[i] = pData->items()[i];	//跳过相应资源的头部,头部之后就是TOOLBAR相应的ID号	
	BOOL bResult = SetButtons(pItems, pData->wItemCount);//根据资源当中的ID号创建相应的TBBUTTON,然后发送TB_ADDBUTTONS添加按钮
	delete[] pItems;

	if (bResult)
	{
		CSize sizeImage(pData->wWidth, pData->wHeight);
		CSize sizeButton(pData->wWidth + 7, pData->wHeight + 7);	
  SetSizes(sizeButton, sizeImage);//设置图片和按钮的大小
		bResult = LoadBitmap(lpszResourceName);//类似于LoadToolBar函数的实现
	}

	UnlockResource(hGlobal);
	FreeResource(hGlobal);

	return bResult;
}
BOOL CToolBar::SetButtons(const UINT* lpIDArray, int nIDCount)
{
	int nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0);
	while (nCount--)
		VERIFY(DefWindowProc(TB_DELETEBUTTON, 0, 0));//清空原有的TOOLBAR项目

	TBBUTTON button; memset(&button, 0, sizeof(TBBUTTON));
	button.iString = -1;
	if (lpIDArray != NULL)
	{
		int iImage = 0;
		for (int i = 0; i < nIDCount; i++)
		{
			button.fsState = TBSTATE_ENABLED;
			if ((button.idCommand = *lpIDArray++) == 0)//先将ID设置到idCommand当中,然后进行if判断
			{
				// separator
				button.fsStyle = TBSTYLE_SEP;
				// width of separator includes 8 pixel overlap
				ASSERT(_afxComCtlVersion != -1);
				if ((GetStyle() & TBSTYLE_FLAT) || _afxComCtlVersion == VERSION_IE4)
					button.iBitmap = 6;
				else
					button.iBitmap = 8;
			}
			else
			{
				button.fsStyle = TBSTYLE_BUTTON;
				button.iBitmap = iImage++;
			}
			if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button))
				return FALSE;
		}
	}
	else
	{
		button.fsState = TBSTATE_ENABLED;
		for (int i = 0; i < nIDCount; i++)
		{
			ASSERT(button.fsStyle == TBSTYLE_BUTTON);
			if (!DefWindowProc(TB_ADDBUTTONS, 1, (LPARAM)&button))
				return FALSE;
		}
	}
	m_nCount = (int)DefWindowProc(TB_BUTTONCOUNT, 0, 0);
	m_bDelayedButtonLayout = TRUE;
	return TRUE;
}
上面代码的主要部分都加了注释,和上面的win32相对比很相似,只不过两者的ID和图片的来源不相同而已。同样的STATUSBAR的创建和TOOLBAR的创建很类似,主要调用函数SetIndicators。
BOOL CStatusBar::SetIndicators(const UINT* lpIDArray, int nIDCount)
{
	if (!AllocElements(nIDCount, sizeof(AFX_STATUSPANE)))//将之前的AFX_STATUSPANE给清除掉,并且同时穿件新的AFX_STATUSPANE
		return FALSE;

	BOOL bResult = TRUE;
	if (lpIDArray != NULL)
	{
		HFONT hFont = (HFONT)SendMessage(WM_GETFONT);
		CClientDC dcScreen(NULL);
		HGDIOBJ hOldFont = NULL;
		if (hFont != NULL)
			hOldFont = dcScreen.SelectObject(hFont);

		AFX_STATUSPANE* pSBP = _GetPanePtr(0);
		for (int i = 0; i < nIDCount; i++)
		{
			pSBP->nID = *lpIDArray++;
			pSBP->nFlags |= SBPF_UPDATE;
			if (pSBP->nID != 0)//当ID号等于0的时候,实际上是一个分隔符
			{
				if (!pSBP->strText.LoadString(pSBP->nID))
				{
					bResult = FALSE;
					break;
				}
				pSBP->cxText = dcScreen.GetTextExtent(pSBP->strText).cx;
		
				if (!SetPaneText(i, pSBP->strText, FALSE))
				{
					bResult = FALSE;
					break;
				}
			}
			else
			{
				pSBP->cxText = ::GetSystemMetrics(SM_CXSCREEN)/4;
				if (i == 0)
					pSBP->nStyle |= (SBPS_STRETCH | SBPS_NOBORDERS);
			}
			++pSBP;
		}
		if (hOldFont != NULL)
			dcScreen.SelectObject(hOldFont);
	}
	UpdateAllPanes(TRUE, TRUE);
	return bResult;
}
struct AFX_STATUSPANE
{
	UINT    nID;        // IDC of indicator: 0 => normal text area
	int     cxText;     // 字符串的宽度
	UINT    nStyle;     // style flags (SBPS_*)
	UINT    nFlags;     // state flags (SBPF_*)
	CString strText;    // 
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值