工具条和状态条都属于主窗口的子窗口,所以创建的时机是在主窗口的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; //
};