WTL port for “Simplest Checkable Groupbox Class”
对文章 “Simplest Checkable Groupbox Class” 所介绍的 “CheckableGroupbox” MFC 类的 WTL 移植, 附带例子程序.
原文参见: http://www.codeproject.com/KB/miscctrl/simplest_checkable_group.aspx
俺的移植类的下载地址是: atlcheckablegroupbox
类 CCheckableGroupBox 的源码在这里:
- //
- // port from the article "Simplest Checkable Groupbox Class"
- //
- // http://www.codeproject.com/KB/miscctrl/simplest_checkable_group.aspx
- //
- #pragma once
- #include <list>
- #include <map>
- #include <atlmisc.h>
- namespace WTL
- {
- class CWndButtonSimple : public CWindowImpl< CWndButtonSimple, CButton>
- {
- public:
- DECLARE_WND_SUPERCLASS( _T("WTL_WndButtonSimple"), GetWndClassName() )
- BEGIN_MSG_MAP( CWndButtonSimple )
- END_MSG_MAP()
- };
- #define ID_CHKBOX_TITLE 0xFFFF
- #define WM_CUST_GETGROUPID (WM_USER + 1)
- template< typename T, typename TBase = CButton, typename TWinTraits = CControlWinTraits >
- class CCheckableGroupBoxT: public CWindowImpl<T, TBase, TWinTraits>
- {
- public:
- CCheckableGroupBoxT ()
- {
- m_nGroupID = 0;
- }
- DECLARE_WND_SUPERCLASS(_T("WTL_CCheckableGroupBoxT"), TBase::GetWndClassName())
- typedef CWindowImpl<T, TBase, TWinTraits> parentClass;
- typedef CCheckableGroupBoxT<T, TBase, TWinTraits> thisClass;
- static std::map<HWND, thisClass *> sm_theKind;
- BEGIN_MSG_MAP(thisClass)
- MESSAGE_HANDLER(WM_ENABLE, OnWmEnable)
- MESSAGE_HANDLER(WM_SETFOCUS, OnWmSetFocus)
- MESSAGE_HANDLER(WM_CUST_GETGROUPID, OnWmCustGetGroupID)
- COMMAND_ID_HANDLER(ID_CHKBOX_TITLE, OnCmdChkBoxTitleClicked)
- MESSAGE_HANDLER(BM_GETCHECK, OnBmGetCheck)
- MESSAGE_HANDLER(BM_SETCHECK, OnBmSetCheck)
- DEFAULT_REFLECTION_HANDLER()
- END_MSG_MAP()
- BOOL SubclassWindow( HWND hWnd )
- {
- BOOL bRet = parentClass::SubclassWindow( hWnd );
- if (bRet) {
- _Init();
- }
- return bRet;
- }
- HWND Create(HWND hWndParent, _U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
- DWORD dwStyle = 0, DWORD dwExStyle = 0,
- _U_MENUorID MenuOrID = 0U, LPVOID lpCreateParam = NULL)
- {
- HWND hWnd = parentClass::Create(hWndParent, rect, szWindowName,
- dwStyle, dwExStyle, MenuOrID, lpCreateParam);
- if (hWnd) {
- _Init();
- }
- return hWnd;
- }
- void _Init()
- {
- sm_theKind[m_hWnd] = this;
- }
- LRESULT OnWmEnable(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandeld)
- {
- uMsg, wParam, lParam, bHandeld;
- LRESULT lr = DefWindowProc(uMsg, wParam, lParam);
- //disable all child controls if group is disabled
- m_TitleBox.EnableWindow( (BOOL) wParam );
- CheckGroupboxControls();
- return lr;
- }
- LRESULT OnWmSetFocus(UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL & bHandeld)
- {
- uMsg, wParam, lParam, bHandeld;
- m_TitleBox.SetFocus();
- bHandeld = FALSE;
- return 0;
- }
- LRESULT OnWmCustGetGroupID(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandeld)
- {
- uMsg, wParam, lParam, bHandeld;
- if (lParam) {
- UINT * pGroupID = (UINT *) lParam;
- *pGroupID = GetGroupID();
- }
- bHandeld = FALSE;
- return 0;
- }
- LRESULT OnCmdChkBoxTitleClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
- {
- wNotifyCode, wID, hWndCtl, bHandled;
- CheckGroupboxControls();
- UINT style = m_TitleBox.GetButtonStyle();
- if(style == BS_AUTORADIOBUTTON)
- {
- CRect rcWnd, rcTest;
- CWindow pWnd = ::GetWindow(GetParent(), GW_CHILD);
- while (pWnd.IsWindow())
- {
- std::map<HWND, thisClass *>::iterator it = sm_theKind.find(pWnd);
- if( it != sm_theKind.end() )
- {
- UINT uiGroupID = 0;
- ::SendMessage(pWnd, WM_CUST_GETGROUPID, 0, (LPARAM)&uiGroupID);
- if(uiGroupID == m_nGroupID && pWnd.m_hWnd != this->m_hWnd)
- {
- ::SendMessage(pWnd, (UINT)BM_SETCHECK, (WPARAM)(int)0, (LPARAM)0);
- }
- }
- pWnd = pWnd.GetWindow(GW_HWNDNEXT);
- }
- }
- ::SendMessage(GetParent(),
- WM_COMMAND,
- MAKEWPARAM(::GetDlgCtrlID(m_hWnd), BN_CLICKED),
- (LPARAM)m_hWnd);
- return 0;
- }
- LRESULT OnBmGetCheck(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandeld)
- {
- uMsg, wParam, lParam, bHandeld;
- bHandeld = FALSE;
- return GetCheck();
- }
- LRESULT OnBmSetCheck(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandeld)
- {
- uMsg, wParam, lParam, bHandeld;
- bHandeld = FALSE;
- SetCheck((int)wParam);
- return 0;
- }
- // Attributes
- public:
- void SetTitleStyle(UINT style = BS_AUTOCHECKBOX)
- {
- TCHAR strText[MAX_PATH] = { 0 };
- GetWindowText(strText, _countof(strText));
- CClientDC dc(m_hWnd);
- CFontHandle pOldFont = dc.SelectFont(GetFont());
- SIZE czText = { 0 };
- dc.GetTextExtent(strText, lstrlen(strText), &czText);
- dc.SelectFont(pOldFont);
- // Add some space for the checkbox and at the end
- czText.cx += 25;
- // Move the checkbox on top of the groupbox
- CRect rc;
- GetWindowRect(rc);
- this->ScreenToClient(rc);
- rc.left += 5;
- rc.right = rc.left + czText.cx;
- rc.bottom = rc.top + czText.cy;
- if(style == BS_AUTOCHECKBOX || style == BS_AUTORADIOBUTTON)
- {
- m_TitleBox.Create(m_hWnd, rc, strText, style|WS_CHILD, 0, ID_CHKBOX_TITLE);
- m_TitleBox.SetFont(GetFont());
- m_TitleBox.ShowWindow(SW_SHOW);
- }
- }
- int GetCheck() const
- {
- if(::IsWindow(m_TitleBox))
- {
- return m_TitleBox.GetCheck();
- }
- return -1;
- }
- void SetCheck(int nCheck)
- {
- if(::IsWindow(m_TitleBox))
- {
- m_TitleBox.SetCheck(nCheck);
- CheckGroupboxControls();
- }
- }
- void SetGroupID(UINT nGroup)
- {
- m_nGroupID = nGroup;
- }
- UINT GetGroupID() const
- {
- return m_nGroupID;
- }
- //
- void CheckGroupboxControls()
- {
- int nCheck = m_TitleBox.GetCheck();
- CRect rcGroupbox;
- GetWindowRect(rcGroupbox);
- std::list<CWindow> mapChkableGroupWnds;
- // Get first child control
- CWindow pWnd = ::GetWindow(GetParent(), GW_CHILD);
- CRect rcWnd, rcTest;
- while (pWnd.IsWindow())
- {
- pWnd.GetWindowRect(rcWnd);
- if (rcTest.IntersectRect(rcGroupbox, rcWnd) &&
- (rcTest != rcGroupbox) &&
- pWnd.m_hWnd != this->m_hWnd &&
- pWnd.m_hWnd != m_TitleBox.m_hWnd)
- {
- std::map<HWND, thisClass *>::iterator it = sm_theKind.find(pWnd);
- if( it != sm_theKind.end() )
- {
- mapChkableGroupWnds.push_back(pWnd);
- }
- pWnd.EnableWindow(nCheck == 1 && m_TitleBox.IsWindowEnabled());
- }
- pWnd = pWnd.GetWindow(GW_HWNDNEXT);
- }
- std::list<CWindow>::iterator iter;
- for(iter=mapChkableGroupWnds.begin(); iter!=mapChkableGroupWnds.end(); ++iter)
- {
- UINT uiState = ::SendMessage(*iter, (UINT)BM_GETCHECK, 0, 0);
- ::SendMessage(*iter, (UINT)BM_SETCHECK, (WPARAM)(int)uiState, (LPARAM)0);
- }
- }
- CWndButtonSimple m_TitleBox; // Could be check box or radio box
- UINT m_nGroupID; //Radio button holds same group id.
- };
- template< typename T, typename TBase, typename TWinTraits>
- std::map<HWND, CCheckableGroupBoxT<T, TBase, TWinTraits> * > CCheckableGroupBoxT<T, TBase, TWinTraits>::sm_theKind;
- class CCheckableGroupBox : public CCheckableGroupBoxT<CCheckableGroupBox>
- {
- public:
- DECLARE_WND_SUPERCLASS(_T("WTL_CCheckableGroupBox"), GetWndClassName())
- };
- }; // namespace WTL

你好, 我用这个类的时候在xp主题下, GroupBox的标题会出现文字重叠的现象, 请教一下怎么解决呢?
解决了,191行Create的时候把加入WS_EX_TRANSPARENT标志貌似就可以了.
m_TitleBox.Create(m_hWnd, rc, strText, style|WS_CHILD, WS_EX_TRANSPARENT, ID_CHKBOX_TITLE);
多谢楼主提供这个类, 省了我们不少事
@mitsui
谢谢,但我在 win 7 下测试, 没有你说的那种现象.
@free2000fly
我也很奇怪, 我在win7下测试也很正常, xp下如果是”windows经典主题”也没问题, 但是换了主题之后就出来那种文字重叠的情况了.
第二个就是博主压缩包里的demo程序无论在哪运行显示都很正常, 仔细看了一下博主的demo程序对话框上控件都是windows经典风格的, 我是vs2008新建的工程, 对话框上控件都是xp风格的, 猜测原因可能是我编译资源文件的时候默认编译成了xp风格了吧.