工具栏的工作原理就是:首先在父窗口上创建一个子窗口,然后在一个子窗口上创建不同ID的按钮,当用户点击某个按钮时,就会以一个命令的方式通知父窗口:我被点击了。所以我沿着这个思路,制作了一个自定义皮肤的工具栏。
工具栏效果展现:
该工具栏包含了:①自定义背景图片、②自定义按钮图片、③自定义ToolTips、④动态增加按钮,等几个主要功能。当鼠标移动到某个工具栏按钮上上时,会动态切换按钮状态,并出现自定义的ToolTips。
实现步骤一(创建)
创建一个名为CCustomCommandBar的子窗口,指定窗口类型为:WS_CHILD | WS_VISIBLE即可。
class CCustomCommandBar : public CWindowImpl<CCustomCommandBar>创建函数:
// Create command bar. HWND Create(const HWND wndParent, const UINT nID) { if (::IsWindow(wndParent) && nID > 0) { m_wndParent = wndParent; CRect rc(0, 0, 1, 1); return CWindowImpl<CCustomCommandBar>::Create(wndParent, rc, _T(""), WS_CHILD | WS_VISIBLE, 0, nID); } ATLASSERT(FALSE); return NULL; }
实现步骤二(设置工具栏背景)
找一张PNG图片,然后用其来做工具栏的背景,我没有刻意的去指定背景图片尺寸,一定要精确的与预定的工具栏吻合。而是找了一张较大的图,进行部分裁切。这样既可以保证动态增加一些按钮后,图像不失真。
// Load background image. BOOL LoadBackgroundImage(const UINT nImageID) { m_Lock.Lock(); m_imgBack.Load_Image(nImageID, _T("PNG")); if (m_imgBack.m_pImage != NULL) { m_Lock.Unlock(); return TRUE; } m_Lock.Unlock(); ATLASSERT(FALSE); return FALSE; }工具栏进行重绘只是进行简单的贴图,而工具栏尺寸,将由按钮的数量决定。
// Draw background image when got WM_PAINT message. void DoPaint(Graphics& g) { m_Lock.Lock(); if (m_imgBack.m_pImage != NULL) { g.DrawImage(m_imgBack.m_pImage, 0, 0); } m_Lock.Unlock(); }实现步骤三(添加按钮)
由于按钮是由工具栏动态创建的,所以只需传进按钮的ID、图片的ID并记录好创建的按钮顺序即可。当创建好按钮后,就重新计算每个按钮的位置,并重置工具栏的大小。
// Insert button. BOOL InsertButton(const UINT nButtonID, const UINT nImageID, const int nImageNum, const BUTTONIMAGEINDEX BtnImgIndex, const CString& strToolTip) { if (IsWindow() && nButtonID > 0 && nImageID > 0 && nImageNum > 0) { // First, create button. CCustomButton* pButton = new CCustomButton(); HWND wndButton = pButton->Create(m_hWnd, nButtonID); ATLASSERT(::IsWindow(wndButton)); pButton->LoadBitmap(nImageID, nImageNum); pButton->SetImages(BtnImgIndex.nNormal, BtnImgIndex.nSelected, BtnImgIndex.nHot, BtnImgIndex.nDisable); pButton->SetToolTipText(strToolTip); // Second, record created button info. BUTTONINFO* pBtnInfo = (BUTTONINFO*)malloc( sizeof(BUTTONINFO) ); pBtnInfo->nID = nButtonID; pBtnInfo->pButton = pButton; m_szButtons.Add(pBtnInfo); // Lastly, move button and record button position. MoveButtons(); return TRUE; } ATLASSERT(FALSE); return FALSE; }实现步骤四(计算工具栏尺寸并移动按钮)
根据每个按钮的间隔计图片宽度,计算工具栏的长度。然后根据按钮图片的高度,来计算工具栏的高度。计算完成后,先重置工具栏的尺寸,然后再依次移动按钮。
重置工具栏尺寸:
// Move and record all buttons to specified position. void MoveButtons() { // First, Calculate all buttons position. for (int nIndex = 0; nIndex < m_szButtons.GetSize(); ++nIndex) { CRect rcPos; m_szButtons[nIndex]->pButton->GetClientRect(&rcPos); int X = 0; int Y = m_nVerSpace; // calculate first button. if (0 == nIndex) { X = m_nHorSpace; } // calculate left button X position via previous button. else { X = m_szButtons[nIndex - 1]->rcPos.right + m_nBtnSpace; } rcPos.MoveToXY(X, Y); m_szButtons[nIndex]->rcPos = rcPos; } // Second, resize command bar window via all buttons position. ResizeCommandBar(); // Lastly, move buttons. for (int nIndex = 0; nIndex < m_szButtons.GetSize(); ++nIndex) { m_szButtons[nIndex]->pButton->MoveWindow(m_szButtons[nIndex]->rcPos, FALSE); } }移动按钮:
// Resize command bar via buttons‘ position. BOOL ResizeCommandBar() { int nHeight = m_nVerSpace * 2 + m_szButtons[0]->rcPos.Height(); // Calculate width via last button‘s position. int nWidth = m_szButtons[m_szButtons.GetSize() - 1]->rcPos.right + m_nHorSpace; if (nHeight > 0 && nWidth > 0) { ResizeClient(nWidth, nHeight); return TRUE; } ATLASSERT(FALSE); return FALSE; }实现步骤五(为父窗口设置点击消息反馈)
我们可以使用WM_COMMAND来通知父窗口:工具栏某个按钮被点击了。
BEGIN_MSG_MAP(CCustomCommandBar) MESSAGE_HANDLER(WM_COMMAND, OnCommand) REFLECT_NOTIFICATIONS() END_MSG_MAP()
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) { if( lParam != 0 && ::GetParent((HWND) lParam) == m_hWnd ) { ::SendMessage(GetParent(), uMsg, wParam, lParam); } return 0; }小结:
类似工具栏这些控件,比如CReBar,都可以用子窗口的形式进行制作。这样做的好处就是:相对比较简单,不需要大费周章的去控制CToolBar原有的一些自绘方法。并且可以增加一些自定义元素在里面。
下载链接:
http://download.csdn.net/detail/renstarone/6973195