实现一个 WTL 带菜单的按钮 CMenuButton
今天将 codeproject 上的一个 MFC 实现的菜单按钮移植到 WTL 下. 测试后发现工作很好.
现在将其用法简述如下.
- 用 VC 的 WTL wizard 创建一个对话框程序, 然后将头文件 atlmenubtn.h 包含到 stdafx.h 文件内.
- 在主界面的对话框资源内添加一个按钮,
- 在 CMainDlg 类是消息循环添加 REFLECT_NOTIFICATIONS() 宏, 这个相当重要, 不然按钮将不会被显示出来.
- 在 CMainDlg 类添加 CMenuButton m_btn; 成员变量, 然后就可以用 m_btn 变量子类化刚才添加的按钮, 使用 SubclassWindow 函数; 或者创建一个, 使用 Create 函数.
- 然后就可以添加菜单项了, 响应菜单命令的消息. 等等等等. 具体细节请看代码.
源代码下载链接
最后的运行效果如下:

头文件 “atlmenubtn.h” 的源码如下, 全文张贴:
下载: atlmenubtn.h
- //
- // based on the class CCoolBtn
- // http://www.codeproject.com/KB/buttons/pushmenubutton.aspx
- //
- #if !defined(AFX_MENUBTN_H__3A90681F_CE5F_11D3_808C_005004D6CF93__INCLUDED_)
- #define AFX_MENUBTN_H__3A90681F_CE5F_11D3_808C_005004D6CF93__INCLUDED_
- #if _MSC_VER > 1000
- #pragma once
- #endif // _MSC_VER > 1000
- #ifndef __cplusplus
- #error ATL requires C++ compilation (use a .cpp suffix)
- #endif
- #ifndef __ATLCTRLS_H__
- #error atlmenubtn.h requires atlctrls.h to be included first
- #endif
- #ifndef __ATLMISC_H__
- #error atlmenubtn.h requires atlmisc.h to be included first
- #endif
- #ifndef __ATLFRAME_H__
- #error atlmenubtn.h requires atlframe.h to be included first
- #endif
- #define nDropBtnWidth 16
- /////////////////////////////////////////////////////////////////////////////
- // CMenuButtonT window
- template <class T, class TBase = CButton, class TWinTraits = CControlWinTraits>
- class CMenuButtonT
- : public CWindowImpl<T, TBase, TWinTraits>
- , public COwnerDraw<T>
- {
- public:
- typedef CMenuButtonT<T, TBase, TWinTraits> thisClass;
- typedef CWindowImpl<T, TBase, TWinTraits> baseClass;
- typedef COwnerDraw<T> ownerDrawClass;
- DECLARE_WND_SUPERCLASS(_T("CMenuButtonT"), TBase::GetWndClassName());
- BEGIN_MSG_MAP(thisClass)
- CHAIN_MSG_MAP_ALT(ownerDrawClass, 1)
- MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
- MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
- MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
- MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
- MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
- MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSysColorChange)
- DEFAULT_REFLECTION_HANDLER()
- END_MSG_MAP()
- CMenuButtonT()
- {
- m_bPushed = FALSE;
- m_bLoaded = FALSE;
- m_bMenuPushed = FALSE;
- m_bMenuLoaded = FALSE;
- m_bDefaultBtn = FALSE;
- ZeroMemory(&m_bm, sizeof(m_bm));
- m_menu.CreatePopupMenu();
- }
- virtual ~CMenuButtonT()
- {
- }
- BOOL SubclassWindow(HWND hWnd)
- {
- BOOL bResult = baseClass::SubclassWindow(hWnd);
- m_pParentWnd = GetParent();
- // Force BS_OWNERDRAW during subclassing
- ModifyStyle(0, BS_OWNERDRAW); // Enforce
- return bResult;
- }
- // Create the button
- HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,
- DWORD dwStyle = 0, DWORD dwExStyle = 0,
- UINT nID = 0, LPVOID lpCreateParam = NULL)
- {
- dwStyle |= BS_OWNERDRAW; // Enforce
- m_pParentWnd = hWndParent;
- return baseClass::Create(hWndParent, rcPos, szWindowName, dwStyle, dwExStyle, nID, lpCreateParam);
- }
- // Sets the button image, COLORREF crMask specifics the transparency
- BOOL SetButtonImage(HINSTANCE hInstance, UINT nResourceId, COLORREF crMask)
- {
- // The ID must exist also as a bitmap resource!!!
- {
- ATLASSERT(m_btnImage.m_hBitmap == NULL);
- m_btnImage.m_hBitmap = ::LoadBitmap(hInstance, MAKEINTRESOURCE(nResourceId));
- ATLASSERT(m_btnImage.m_hBitmap);
- }
- m_btnImage.GetBitmap(m_bm);
- m_IL.Create( nResourceId, m_bm.bmWidth, 1, crMask );
- m_bLoaded = TRUE;
- m_crMask = crMask;
- HBITMAP bmTemp = NULL;
- COLORMAP mapColor = { 0 };
- mapColor.from = crMask;
- mapColor.to = RGB(255,255,255);
- bmTemp = (HBITMAP)::CreateMappedBitmap(hInstance, nResourceId, IMAGE_BITMAP, &mapColor, 1);
- m_hbmpDisabled = (HBITMAP)::CopyImage(bmTemp, IMAGE_BITMAP, m_bm.bmWidth, m_bm.bmHeight, LR_COPYDELETEORG);
- return m_bLoaded;
- }
- // Adds a menu item and id to our menu.
- BOOL AddMenuItem(UINT nMenuId, const CString strMenu, UINT nMenuFlags)
- {
- BOOL bRet = m_menu.AppendMenu(nMenuFlags | MF_STRING, nMenuId, (LPCTSTR)strMenu);
- m_bMenuLoaded |= bRet;
- return bRet;
- }
- // Enable/Disable a menu item
- BOOL EnableMenuItem(UINT nMenuId,const CString strMenu, BOOL nEnable)
- {
- BOOL bRet = m_menu.EnableMenuItem(nMenuId , nEnable);
- m_bMenuLoaded |= bRet;
- return bRet;
- }
- BOOL RemoveMenuItem(UINT nMenuId,const CString strMenu, UINT nMenuFlags)
- {
- BOOL bRet = m_menu.RemoveMenu( nMenuId,nMenuFlags | MF_STRING);
- m_bMenuLoaded |= bRet;
- return bRet;
- }
- // Adds Image to menu
- void AddImage( int nIndex, CBitmap& bmpEnabled, CBitmap& bmpDisabled )
- {
- m_menu.SetMenuItemBitmaps( nIndex, MF_BYPOSITION, &bmpEnabled, &bmpDisabled );
- }
- // Allow menu flags to be changed dynamically
- void ChangeMenuFlags(UINT nMenuId, UINT nMenuFlags)
- {
- m_menu.CheckMenuItem(nMenuId, nMenuFlags | MF_BYCOMMAND);
- }
- // Call this to allow the button to a act as a default button
- void SetDefaultButton(BOOL bDefault)
- {
- m_bDefaultBtn = bDefault;
- }
- // Attributes
- protected:
- CMenu m_menu;
- CBitmap m_btnImage;
- CImageList m_IL;
- BOOL m_bPushed;
- BOOL m_bMenuPushed;
- BOOL m_bMenuLoaded;
- BOOL m_bDefaultBtn;
- BOOL m_bLoaded;
- BITMAP m_bm;
- CWindow m_pParentWnd;
- COLORREF m_crMask;
- CBitmap m_hbmpDisabled;
- protected:
- // Draws drop down arrow, we could use DrawFrameControl - a bit too messy
- void DrawArrow(HDC hDC, CPoint ArrowTip)
- {
- CDCHandle pDC(hDC);
- CPoint ptDest;
- CPenHandle pPen = pDC.GetCurrentPen();
- LOGPEN logPen = { 0 };
- pPen.GetLogPen(logPen);
- pDC.SetPixel(ArrowTip, logPen.lopnColor);
- ArrowTip -= CPoint(1,1);
- pDC.MoveTo(ArrowTip);
- ptDest = ArrowTip;
- ptDest += CPoint(3,0);
- pDC.LineTo(ptDest);
- ArrowTip -= CPoint(1,1);
- pDC.MoveTo(ArrowTip);
- ptDest = ArrowTip;
- ptDest += CPoint(5,0);
- pDC.LineTo(ptDest);
- ArrowTip -= CPoint(1,1);
- pDC.MoveTo(ArrowTip);
- ptDest = ArrowTip;
- ptDest += CPoint(7,0);
- pDC.LineTo(ptDest);
- }
- // Helper function to test for menu button hit ...
- BOOL HitMenuBtn(CPoint point)
- {
- if (!m_bMenuLoaded)
- {
- // Don't allow menu button drop down if no menu items are loaded
- return FALSE;
- }
- ClientToScreen(&point);
- CRect rect;
- GetWindowRect(rect);
- rect.left = rect.right - nDropBtnWidth;
- return rect.PtInRect(point);
- }
- public:
- // Called in response to draw the button
- void DrawItem(LPDRAWITEMSTRUCT lpDIS)
- {
- if (lpDIS->CtlType != ODT_BUTTON)
- return;
- CFontHandle pFont = AtlGetStockFont(DEFAULT_GUI_FONT);
- CDC dcMem;
- CBitmap bmp;
- CRect btnRect(lpDIS->rcItem);
- CRect trueRect(btnRect);
- CDCHandle pDC(lpDIS->hDC);
- ////////////////////////////////////////
- // Button Background //
- ////////////////////////////////////////
- CBrush brsh;
- brsh.CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
- pDC.FillRect(trueRect, brsh);
- BOOL bDisabled = ODS_DISABLED & lpDIS->itemState;
- if (m_bDefaultBtn)
- btnRect.DeflateRect(1,1);
- CRect rectFocus(btnRect);
- rectFocus.DeflateRect(4,4);
- if (!m_bMenuPushed)
- rectFocus.OffsetRect(m_bPushed,m_bPushed);
- rectFocus.right -= nDropBtnWidth;
- ////////////////////////////////////////
- // Button in a normal state //
- ////////////////////////////////////////
- if (!m_bPushed || m_bMenuPushed)
- pDC.DrawFrameControl(&btnRect,DFC_BUTTON,DFCS_BUTTONPUSH);
- ////////////////////////////////////////
- // Default Button State //
- ////////////////////////////////////////
- if ((m_bDefaultBtn || m_bPushed) && !bDisabled)
- {
- pDC.FrameRect(&lpDIS->rcItem, AtlGetStockBrush(BLACK_BRUSH) );
- if (m_bPushed && !m_bMenuPushed)
- pDC.FrameRect(&btnRect, AtlGetStockBrush(BLACK_BRUSH) );
- }
- ////////////////////////////////////////
- // State Focus //
- ////////////////////////////////////////
- if (lpDIS->itemState & ODS_FOCUS || m_bPushed)
- if (!m_bMenuPushed)
- pDC.DrawFocusRect(&rectFocus);
- ////////////////////////////////////////
- // Action Focus //
- ////////////////////////////////////////
- if ((lpDIS->itemAction & ODA_FOCUS))
- if (!m_bMenuPushed)
- pDC.DrawFocusRect(&rectFocus);
- ////////////////////////////////////////
- // Draw out bitmap //
- ////////////////////////////////////////
- // Draw out bitmap
- if (m_bLoaded)
- {
- if (!bDisabled)
- {
- ATLASSERT(pDC.m_hDC != NULL);
- CPoint pt(6+m_bPushed, 6+m_bPushed);
- CSize sz(m_bm.bmWidth, m_bm.bmHeight);
- CPoint ptOrigin(0,0);
- UINT fStyle = ILD_NORMAL;
- DWORD dwRop = SRCCOPY;
- COLORREF rgbBack = CLR_DEFAULT;
- COLORREF rgbFore = CLR_DEFAULT;
- DWORD fState = ILS_NORMAL;
- DWORD Frame = ILS_NORMAL; // ILS_PULSE;
- COLORREF crEffect = CLR_DEFAULT;
- IMAGELISTDRAWPARAMS drawing = { 0 };
- drawing.i = 0;
- drawing.hdcDst = pDC.m_hDC;
- drawing.x = pt.x;
- drawing.y = pt.y;
- drawing.cx = sz.cx;
- drawing.cy = sz.cy;
- drawing.xBitmap = ptOrigin.x;
- drawing.yBitmap = ptOrigin.y;
- drawing.rgbBk = rgbBack;
- drawing.rgbFg = rgbFore;
- drawing.fStyle = fStyle;
- drawing.dwRop = dwRop;
- #if (_WIN32_IE >= 0x501)
- drawing.fState = fState;
- drawing.Frame = Frame;
- drawing.crEffect = crEffect;
- #endif
- // ATLASSERT(m_IL.m_hImageList != NULL);
- drawing.cbSize = sizeof(IMAGELISTDRAWPARAMS);
- drawing.himl = m_IL.m_hImageList;
- // m_IL.DrawIndirect(pDC,0,CPoint(6+m_bPushed,6+m_bPushed),
- // CSize(m_bm.bmWidth, m_bm.bmHeight), CPoint(0,0),ILD_NORMAL);
- m_IL.DrawIndirect(&drawing);
- }
- else
- {
- pDC.DrawState(CPoint(6+m_bPushed,6+m_bPushed),
- CSize(m_bm.bmWidth, m_bm.bmHeight),
- m_hbmpDisabled, DST_BITMAP | DSS_DISABLED);
- }
- }
- ////////////////////////////////////////
- // Draw out text //
- ////////////////////////////////////////
- CFontHandle fontOld = pDC.SelectFont(pFont);
- CRect rectText(rectFocus);
- rectFocus.left += m_bm.bmWidth + 2;
- CString strCaption;
- GetWindowText(strCaption.GetBuffer(MAX_PATH), MAX_PATH);
- strCaption.ReleaseBuffer();
- pDC.SetBkMode(TRANSPARENT);
- pDC.SetBkColor(GetSysColor(COLOR_BTNFACE));
- if (ODS_DISABLED & lpDIS->itemState)
- {
- rectFocus.OffsetRect(1,1);
- pDC.SetTextColor(GetSysColor(COLOR_WINDOW));
- pDC.DrawText((LPCTSTR)strCaption, strCaption.GetLength(), rectFocus, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
- rectFocus.OffsetRect(-1,-1);
- pDC.SetTextColor(GetSysColor(COLOR_GRAYTEXT));
- pDC.DrawText((LPCTSTR)strCaption, strCaption.GetLength(), rectFocus,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
- }
- else
- {
- pDC.SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
- pDC.DrawText((LPCTSTR)strCaption, strCaption.GetLength(), rectFocus, DT_SINGLELINE|DT_CENTER|DT_VCENTER);
- }
- CRect rectSplit(btnRect);
- rectSplit.DeflateRect(2,2);
- rectSplit.right -= nDropBtnWidth;
- ////////////////////////////////////////
- // Drop down split //
- ////////////////////////////////////////
- CPen brFace;
- brFace.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
- CPenHandle penOld = pDC.SelectPen(brFace);
- pDC.MoveTo(rectSplit.right, rectSplit.top);
- pDC.LineTo(rectSplit.right, rectSplit.bottom);
- CPen brLite;
- brLite.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
- pDC.SelectPen(brLite);
- pDC.MoveTo(rectSplit.right+1 , rectSplit.top);
- pDC.LineTo(rectSplit.right+1, rectSplit.bottom);
- rectSplit.left = rectSplit.right;
- rectSplit.right += nDropBtnWidth;
- CPoint pt(rectSplit.CenterPoint());
- pt += CPoint(m_bPushed,m_bPushed);
- CPen penBlack;
- penBlack.CreatePen(PS_SOLID, 1, bDisabled ? GetSysColor(COLOR_GRAYTEXT) : GetSysColor(COLOR_WINDOWTEXT));
- pDC.SelectPen(penBlack);
- DrawArrow(pDC,pt);
- ////////////////////////////////////////
- // Drop down state //
- ////////////////////////////////////////
- if (m_bMenuPushed && !bDisabled)
- {
- rectSplit.InflateRect(1,1);
- pDC.DrawEdge(rectSplit,BDR_SUNKENOUTER, BF_RECT);
- }
- pDC.SelectPen(penOld);
- pDC.SelectFont(fontOld);
- }
- // Implementation
- public:
- // Generated message map functions
- public:
- LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- uMsg, wParam, lParam, bHandled;
- LRESULT lResult = 0;
- UINT nFlags = (UINT) wParam;
- CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
- if (m_bMenuPushed)
- {
- m_bMenuPushed = FALSE;
- Invalidate();
- return lResult;
- }
- if (HitMenuBtn(point))
- {
- m_bMenuPushed = TRUE;
- SetFocus();
- Invalidate();
- CRect rc;
- GetWindowRect(rc);
- int x = rc.left;
- int y = rc.bottom;
- m_menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON, x,y, m_pParentWnd);
- m_bMenuPushed = FALSE;
- }
- else
- {
- m_bPushed = TRUE;
- }
- Invalidate();
- if (m_bPushed) {
- lResult = DefWindowProc(uMsg, wParam, lParam);
- }
- return lResult;
- }
- LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- uMsg, wParam, lParam, bHandled;
- LRESULT lResult = 0;
- UINT nFlags = (UINT) wParam;
- CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
- if (m_bPushed)
- {
- m_bPushed = FALSE;
- ReleaseCapture();
- Invalidate();
- m_pParentWnd.PostMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM) m_hWnd);
- }
- lResult = DefWindowProc(uMsg, wParam, lParam);
- return lResult;
- }
- LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- uMsg, wParam, lParam, bHandled;
- LRESULT lResult = 0;
- UINT nFlags = (UINT) wParam;
- CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
- if (m_bPushed)
- {
- ClientToScreen(&point);
- if (WindowFromPoint(point) != *this)
- {
- m_bPushed = FALSE;
- ReleaseCapture();
- Invalidate();
- }
- }
- lResult = DefWindowProc(uMsg, wParam, lParam);
- return lResult;
- }
- LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- LRESULT lResult = 0;
- lResult = DefWindowProc(uMsg, wParam, lParam);
- Invalidate();
- return lResult;
- }
- LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- LRESULT lResult = 0;
- lResult = DefWindowProc(uMsg, wParam, lParam);
- return lResult;
- }
- // Called when system colors change, force a button redraw
- LRESULT OnSysColorChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
- {
- LRESULT lResult = 0;
- lResult = DefWindowProc(uMsg, wParam, lParam);
- Invalidate();
- return lResult;
- }
- };
- class CMenuButton : public CMenuButtonT<CMenuButton>
- {
- public:
- DECLARE_WND_SUPERCLASS(_T("CMenuButton"), GetWndClassName());
- protected:
- private:
- };
- /////////////////////////////////////////////////////////////////////////////
- // CMenuButton
- // ========
- //
- // To Use:
- // 1. Create a Bitmap resource 16x15 Pixels normal default size for a toolbar
- // bitmap.
- //
- // 2. Call CMenuButton on Create function.
- //
- // 3. add REFLECT_NOTIFICATIONS() macro to the BEGIN_MSG_MAP(CMainDlg) message handler list
- //
- // 4. Call SetButtonImage specificing the Transparency color reference.
- // (Usally RGB(255, 0, 255) magenta)
- //
- // 5. Add menu items with AddMenuItem using nMenuFlags to add disabled and
- /// seperator menu items
- //
- // 6. Add the appropiate ON_COMMAND handlers in the parent windows message map
- //
- // 7. Enjoy...
- //
- #endif // !defined(AFX_MENUBTN_H__3A90681F_CE5F_11D3_808C_005004D6CF93__INCLUDED_)
一个古老的带位图的菜单 源码

近期评论