1,284 views
首页 > 技术心得 > WTL port for “Simplest Checkable Groupbox Class”

WTL port for “Simplest Checkable Groupbox Class”

2007年12月7日

对文章 “Simplest Checkable Groupbox Class” 所介绍的 “CheckableGroupbox” MFC 类的 WTL 移植, 附带例子程序.

原文参见: http://www.codeproject.com/KB/miscctrl/simplest_checkable_group.aspx

俺的移植类的下载地址是: atlcheckablegroupbox


类 CCheckableGroupBox 的源码在这里:

  1. //
  2. // port from the article "Simplest Checkable Groupbox Class"
  3. //
  4. // http://www.codeproject.com/KB/miscctrl/simplest_checkable_group.aspx
  5. //
  6.  
  7. #pragma once
  8.  
  9. #include <list> 
  10. #include <map> 
  11.  
  12. #include <atlmisc.h> 
  13.  
  14. namespace WTL 
  15. { 
  16.  
  17.     class CWndButtonSimple : public CWindowImpl< CWndButtonSimple, CButton>
  18.     { 
  19.     public:
  20.         DECLARE_WND_SUPERCLASS( _T("WTL_WndButtonSimple"), GetWndClassName() ) 
  21.         BEGIN_MSG_MAP( CWndButtonSimple ) 
  22.         END_MSG_MAP() 
  23.     };
  24.  
  25.  
  26. #define ID_CHKBOX_TITLE     0xFFFF
  27. #define WM_CUST_GETGROUPID  (WM_USER + 1)
  28.  
  29.  
  30.     template< typename T, typename TBase = CButton, typename TWinTraits = CControlWinTraits >
  31.     class CCheckableGroupBoxT: public CWindowImpl<T, TBase, TWinTraits>
  32.     { 
  33.     public:
  34.         CCheckableGroupBoxT () 
  35.         { 
  36.             m_nGroupID = 0;
  37.         } 
  38.  
  39.         DECLARE_WND_SUPERCLASS(_T("WTL_CCheckableGroupBoxT"), TBase::GetWndClassName()) 
  40.  
  41.         typedef CWindowImpl<T, TBase, TWinTraits> parentClass;
  42.         typedef CCheckableGroupBoxT<T, TBase, TWinTraits> thisClass;
  43.  
  44.         static std::map<HWND, thisClass *> sm_theKind;
  45.  
  46.         BEGIN_MSG_MAP(thisClass) 
  47.             MESSAGE_HANDLER(WM_ENABLE,          OnWmEnable) 
  48.             MESSAGE_HANDLER(WM_SETFOCUS,        OnWmSetFocus) 
  49.             MESSAGE_HANDLER(WM_CUST_GETGROUPID, OnWmCustGetGroupID) 
  50.             COMMAND_ID_HANDLER(ID_CHKBOX_TITLE, OnCmdChkBoxTitleClicked) 
  51.  
  52.             MESSAGE_HANDLER(BM_GETCHECK,        OnBmGetCheck) 
  53.             MESSAGE_HANDLER(BM_SETCHECK,        OnBmSetCheck) 
  54.  
  55.             DEFAULT_REFLECTION_HANDLER() 
  56.         END_MSG_MAP() 
  57.  
  58.         BOOL SubclassWindow( HWND hWnd ) 
  59.         { 
  60.             BOOL bRet = parentClass::SubclassWindow( hWnd );
  61.             if (bRet) { 
  62.                 _Init();
  63.             } 
  64.             return bRet;
  65.         } 
  66.  
  67.         HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
  68.             DWORD dwStyle = 0, DWORD dwExStyle = 0,
  69.             _U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL) 
  70.         { 
  71.             HWND hWnd = parentClass::Create(hWndParent, rect, szWindowName,
  72.                 dwStyle, dwExStyle, MenuOrID, lpCreateParam);
  73.             if (hWnd) { 
  74.                 _Init();
  75.             } 
  76.             return hWnd;
  77.         } 
  78.  
  79.         void _Init() 
  80.         { 
  81.             sm_theKind[m_hWnd] = this;
  82.         } 
  83.  
  84.         LRESULT OnWmEnable(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandeld) 
  85.         { 
  86.             uMsg, wParam, lParam, bHandeld;
  87.  
  88.             LRESULT lr = DefWindowProc(uMsg, wParam, lParam);
  89.  
  90.             //disable all child controls if group is disabled
  91.             m_TitleBox.EnableWindow( (BOOL) wParam );
  92.             CheckGroupboxControls();
  93.  
  94.             return lr;
  95.         } 
  96.  
  97.         LRESULT OnWmSetFocus(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandeld) 
  98.         { 
  99.             uMsg, wParam, lParam, bHandeld;
  100.             m_TitleBox.SetFocus();
  101.             bHandeld = FALSE;
  102.             return 0;
  103.         } 
  104.  
  105.         LRESULT OnWmCustGetGroupID(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandeld) 
  106.         { 
  107.             uMsg, wParam, lParam, bHandeld;
  108.             if (lParam) { 
  109.                 UINT * pGroupID = (UINT *) lParam;
  110.                 *pGroupID = GetGroupID();
  111.             } 
  112.             bHandeld = FALSE;
  113.             return 0;
  114.         } 
  115.  
  116.         LRESULT OnCmdChkBoxTitleClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) 
  117.         { 
  118.             wNotifyCode, wID, hWndCtl, bHandled;
  119.  
  120.             CheckGroupboxControls();
  121.             UINT style = m_TitleBox.GetButtonStyle();
  122.             if(style == BS_AUTORADIOBUTTON) 
  123.             { 
  124.                 CRect rcWnd, rcTest;
  125.  
  126.                 CWindow pWnd = ::GetWindow(GetParent(), GW_CHILD);
  127.                 while (pWnd.IsWindow()) 
  128.                 { 
  129.                     std::map<HWND, thisClass *>::iterator it = sm_theKind.find(pWnd);
  130.                     if( it != sm_theKind.end() ) 
  131.                     { 
  132.                         UINT uiGroupID = 0;
  133.                         ::SendMessage(pWnd, WM_CUST_GETGROUPID, 0, (LPARAM)&uiGroupID);
  134.                         if(uiGroupID == m_nGroupID && pWnd.m_hWnd != this->m_hWnd) 
  135.                         { 
  136.                             ::SendMessage(pWnd, (UINT)BM_SETCHECK, (WPARAM)(int)0, (LPARAM)0);
  137.                         } 
  138.                     } 
  139.                     pWnd = pWnd.GetWindow(GW_HWNDNEXT);
  140.                 } 
  141.             }
  142.  
  143.             ::SendMessage(GetParent(),
  144.                 WM_COMMAND,
  145.                 MAKEWPARAM(::GetDlgCtrlID(m_hWnd), BN_CLICKED),
  146.                 (LPARAM)m_hWnd);
  147.  
  148.             return 0;
  149.         } 
  150.  
  151.         LRESULT OnBmGetCheck(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandeld) 
  152.         { 
  153.             uMsg, wParam, lParam, bHandeld;
  154.             bHandeld = FALSE;
  155.             return GetCheck();
  156.         } 
  157.  
  158.         LRESULT OnBmSetCheck(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandeld)
  159.         { 
  160.             uMsg, wParam, lParam, bHandeld;
  161.             bHandeld = FALSE;
  162.             SetCheck((int)wParam);
  163.             return 0;
  164.         } 
  165.  
  166.         // Attributes
  167.     public:
  168.         void SetTitleStyle(UINT style = BS_AUTOCHECKBOX) 
  169.         { 
  170.             TCHAR strText[MAX_PATH] = { 0 };
  171.             GetWindowText(strText, _countof(strText));
  172.  
  173.             CClientDC dc(m_hWnd);
  174.             CFontHandle pOldFont = dc.SelectFont(GetFont());
  175.             SIZE czText = { 0 };
  176.             dc.GetTextExtent(strText, lstrlen(strText), &czText);
  177.             dc.SelectFont(pOldFont);
  178.             // Add some space for the checkbox and at the end
  179.             czText.cx += 25;
  180.  
  181.             // Move the checkbox on top of the groupbox
  182.             CRect rc;
  183.             GetWindowRect(rc);
  184.             this->ScreenToClient(rc);
  185.             rc.left += 5;
  186.             rc.right = rc.left + czText.cx;
  187.             rc.bottom = rc.top + czText.cy;
  188.  
  189.             if(style == BS_AUTOCHECKBOX || style == BS_AUTORADIOBUTTON) 
  190.             { 
  191.                 m_TitleBox.Create(m_hWnd, rc, strText, style|WS_CHILD, 0, ID_CHKBOX_TITLE);
  192.                 m_TitleBox.SetFont(GetFont());
  193.                 m_TitleBox.ShowWindow(SW_SHOW);
  194.             } 
  195.         } 
  196.  
  197.         int GetCheck() const 
  198.         { 
  199.             if(::IsWindow(m_TitleBox)) 
  200.             { 
  201.                 return m_TitleBox.GetCheck();
  202.             } 
  203.             return -1;
  204.         } 
  205.  
  206.         void SetCheck(int nCheck) 
  207.         { 
  208.             if(::IsWindow(m_TitleBox)) 
  209.             { 
  210.                 m_TitleBox.SetCheck(nCheck);
  211.                 CheckGroupboxControls();
  212.             } 
  213.         } 
  214.  
  215.         void SetGroupID(UINT nGroup) 
  216.         { 
  217.             m_nGroupID = nGroup;
  218.         } 
  219.  
  220.         UINT GetGroupID() const 
  221.         { 
  222.             return m_nGroupID;
  223.         } 
  224.  
  225.         //
  226.         void CheckGroupboxControls() 
  227.         { 
  228.             int nCheck = m_TitleBox.GetCheck();
  229.             CRect rcGroupbox;
  230.             GetWindowRect(rcGroupbox);
  231.             std::list<CWindow> mapChkableGroupWnds;
  232.  
  233.             // Get first child control
  234.             CWindow pWnd = ::GetWindow(GetParent(), GW_CHILD);
  235.  
  236.             CRect rcWnd, rcTest;
  237.  
  238.             while (pWnd.IsWindow()) 
  239.             { 
  240.                 pWnd.GetWindowRect(rcWnd);
  241.  
  242.                 if (rcTest.IntersectRect(rcGroupbox, rcWnd) &&
  243.                     (rcTest != rcGroupbox) &&
  244.                     pWnd.m_hWnd != this->m_hWnd &&
  245.                     pWnd.m_hWnd != m_TitleBox.m_hWnd) 
  246.                 { 
  247.                     std::map<HWND, thisClass *>::iterator it = sm_theKind.find(pWnd);
  248.                     if( it != sm_theKind.end() ) 
  249.                     { 
  250.                         mapChkableGroupWnds.push_back(pWnd);
  251.                     } 
  252.                     pWnd.EnableWindow(nCheck == 1 && m_TitleBox.IsWindowEnabled());
  253.                 } 
  254.                 pWnd = pWnd.GetWindow(GW_HWNDNEXT);
  255.             } 
  256.  
  257.             std::list<CWindow>::iterator iter;
  258.             for(iter=mapChkableGroupWnds.begin(); iter!=mapChkableGroupWnds.end(); ++iter) 
  259.             { 
  260.                 UINT uiState = ::SendMessage(*iter, (UINT)BM_GETCHECK, 0, 0);
  261.                 ::SendMessage(*iter, (UINT)BM_SETCHECK, (WPARAM)(int)uiState, (LPARAM)0);
  262.             } 
  263.         } 
  264.  
  265.         CWndButtonSimple    m_TitleBox; // Could be check box or radio box
  266.         UINT                m_nGroupID; //Radio button holds same group id.
  267.     };
  268.  
  269.     template< typename T, typename TBase, typename TWinTraits>
  270.     std::map<HWND, CCheckableGroupBoxT<T, TBase, TWinTraits> * > CCheckableGroupBoxT<T, TBase, TWinTraits>::sm_theKind;
  271.  
  272.     class CCheckableGroupBox : public CCheckableGroupBoxT<CCheckableGroupBox>
  273.     { 
  274.     public:
  275.         DECLARE_WND_SUPERCLASS(_T("WTL_CCheckableGroupBox"), GetWndClassName()) 
  276.     };
  277.  
  278. }// namespace WTL

技术心得 ,

  1. mitsui
    2011年5月13日16:04 | #1

    你好, 我用这个类的时候在xp主题下, GroupBox的标题会出现文字重叠的现象, 请教一下怎么解决呢?

  2. mitsui
    2011年5月13日16:28 | #2

    解决了,191行Create的时候把加入WS_EX_TRANSPARENT标志貌似就可以了.

    m_TitleBox.Create(m_hWnd, rc, strText, style|WS_CHILD, WS_EX_TRANSPARENT, ID_CHKBOX_TITLE);

    多谢楼主提供这个类, 省了我们不少事

  3. 2011年5月13日20:14 | #3

    @mitsui
    谢谢,但我在 win 7 下测试, 没有你说的那种现象.

  4. mitsui
    2011年5月18日20:44 | #4

    @free2000fly
    我也很奇怪, 我在win7下测试也很正常, xp下如果是”windows经典主题”也没问题, 但是换了主题之后就出来那种文字重叠的情况了.
    第二个就是博主压缩包里的demo程序无论在哪运行显示都很正常, 仔细看了一下博主的demo程序对话框上控件都是windows经典风格的, 我是vs2008新建的工程, 对话框上控件都是xp风格的, 猜测原因可能是我编译资源文件的时候默认编译成了xp风格了吧.

  1. 目前还没有任何 trackbacks 和 pingbacks.