WTL 自绘ComboBox带CheckBox
效果图:
原帖:http://www.cnblogs.com/liangbin/articles/2064932.html
通过SetWindowLong自定义ListBox的窗口过程“ComboBoxListBoxProc”,来改变在下拉列表中的一些行为。
但是这么做需要在CheckComboBox类中添加一些静态变量,可以在自定义的窗口过程中调用。
其实可以封装的更简洁些,通过容器窗口,也可以通过子类/超类化,下面代码通过容器窗口实现:
1: //*************************************************************************2: //3: // Copyright (C), 2009-2010, Handy Information Co.,Ltd,All Rights Reserved4: //5: // FileName: CheckComboBox.h6: // Author: yinxf7: // Date: 2012/2012/48: // Description: 带checkbox的ComboBox,自绘9: // 要将ComboBox的Tyep设置为Drop List10: // OwnerDraw设置为Variable11: // Has Strings设置为True12: // Function List:13: // History:14: // <author> <time> <desc>15: // yinxf 2010/07/13 Build16: //17: //*************************************************************************18: #pragma once19:20: class CCheckComboBox21: : public CWindowImpl<CCheckComboBox, CComboBox>22: , public COwnerDraw<CCheckComboBox>23: {24: public:25: CCheckComboBox(void)26: : m_listBox(this, 1)27: , m_bTextUpdated(FALSE)28: {}29: ~CCheckComboBox(void){}30:31: // 设置选中32: int SetCheck(int nIndex, BOOL bCheck)33: {34: int nRet = SetItemData(nIndex, bCheck);35: if ( nRet == CB_ERR )36: return nRet;37:38: // Signal that the text need updating39: m_bTextUpdated = FALSE;40:41: // Redraw the window42: Invalidate(FALSE);43:44: return nRet;45: }46:47: // 获取选中状态48: BOOL GetCheck(int nIndex)49: {50: return GetItemData(nIndex);51: }52:53: // Selects all/unselects all54: void SelectAll(BOOL bCheck = TRUE)55: {56: for ( int i = 0; i < GetCount(); i++ )57: SetCheck(i, bCheck);58: }59:60: protected:61: BEGIN_MSG_MAP(CCheckComboBox)62: MSG_WM_CTLCOLORLISTBOX(OnCtlColorListBox)63: MSG_WM_GETTEXT(OnGetText)64: MSG_WM_GETTEXTLENGTH(OnGetTextLength)65: CHAIN_MSG_MAP_ALT(COwnerDraw<CCheckComboBox>, 1)66: DEFAULT_REFLECTION_HANDLER()67: /////////////////处理ListBox消息/////////////////////////////68: ALT_MSG_MAP(1)69: MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)70: MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)71: MESSAGE_HANDLER(LB_GETCURSEL, OnGetCurSel)72: MESSAGE_HANDLER(WM_RBUTTONDOWN, OnRButtonDown)73: //////////////////////////////////////////////////////////////74: END_MSG_MAP()75:76: BOOL SubclassWindow(HWND hWnd)77: {78: BOOL bRet = __super::SubclassWindow(hWnd);79: if ( bRet )80: {81: // 一旦创建无法修改,要么通过create创建82: // CWindow::ModifyStyle(0, CBS_OWNERDRAWVARIABLE); // 添加自绘属性83: // CWindow::ModifyStyle(CBS_DROPDOWN, CBS_DROPDOWNLIST);84:85: DWORD style = GetStyle();86: // Make sure to use the CBS_DROPDOWNLIST style87: ATLASSERT(style & CBS_DROPDOWNLIST);88: // Make sure to use the CBS_OWNERDRAWVARIABLE style89: ATLASSERT(style & CBS_OWNERDRAWVARIABLE);90: // Use default strings. We need the itemdata to store checkmarks91: ATLASSERT(style & CBS_HASSTRINGS);92: }93: return bRet;94: }95:96: // 自绘97: void DrawItem(LPDRAWITEMSTRUCT lpdis)98: {99: CRect rcCheck = lpdis->rcItem;100: CRect rcText = lpdis->rcItem;101: CString strText;102:103: // 检查是否绘制的静态文本框104: if ( (LONG)lpdis->itemID < 0 ) // *一定要强制转换为有符号性*105: {106: RecalcText();107: strText = m_strTextAll;108: }109: // 下拉列表110: else111: {112: BOOL bCheck = GetCheck(lpdis->itemID);113: GetLBText(lpdis->itemID, strText);114:115: // 绘制checkbox116: TEXTMETRIC txtMetric;117: GetTextMetrics(lpdis->hDC, &txtMetric);118: rcCheck.top += 1;119: rcCheck.left = 0;120: rcCheck.right = rcCheck.left + txtMetric.tmHeight + txtMetric.tmExternalLeading + 6;121: rcCheck.bottom -= 1;122: rcText.left = rcCheck.right;123:124: UINT nState = bCheck ? DFCS_CHECKED : DFCS_BUTTONCHECK;125: DrawFrameControl(lpdis->hDC, rcCheck, DFC_BUTTON, nState);126: }127:128: // 绘制文字129: if ( lpdis->itemState & ODS_SELECTED ) // 设置背景色130: {131: SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT));132: SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));133: }134: else135: {136: SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW));137: SetTextColor(lpdis->hDC, GetSysColor(COLOR_WINDOWTEXT));138: }139:140: // Erase and draw141: ExtTextOut(lpdis->hDC, 0, 0, ETO_OPAQUE, rcText, 0, 0, 0); // ETO_OPAQUE表示当前背景颜色会充满整个矩形框142: DrawText(lpdis->hDC, strText, strText.GetLength(), rcText, DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS);143:144: // 绘制焦点(虚线)145: if ((lpdis->itemState & (ODS_FOCUS|ODS_SELECTED)) == (ODS_FOCUS|ODS_SELECTED))146: DrawFocusRect(lpdis->hDC, &rcText);147: }148:149: LRESULT OnCtlColorListBox(HDC hdc, HWND hwnd)150: {151: ATLTRACE(_T("[%s]0x%x"), _T(__FUNCTION__), hwnd);152:153: if ( NULL == m_listBox.m_hWnd )154: m_listBox.SubclassWindow(hwnd);155:156: return DefWindowProc();157: }158:159: //160: // By adding this message handler, we may use CWindow::GetText()161: //162: int OnGetText(int cchTextMax, LPTSTR lpszText)163: {164: // Make sure the text is updated165: RecalcText();166:167: if (NULL == lpszText)168: return 0;169:170: // Copy the 'fake' window text171: _tcscpy_s(lpszText, cchTextMax, m_strTextAll);172: return m_strTextAll.GetLength();173: }174:175: //176: // By adding this message handler, we may use CWindow::GetTextLength()177: //178: int OnGetTextLength()179: {180: // Make sure the text is updated181: RecalcText();182: return m_strTextAll.GetLength();183: }184:185: ///////////////////////////////////////////////////////////////////////////////////186: // 处理ListBox消息187:188: LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)189: {190: CPoint pt;191: pt.x = LOWORD(lParam);192: pt.y = HIWORD(lParam);193:194: // Compute which index to check/uncheck195: int nItemHeight = GetItemHeight(0);196: int nTopIndex = GetTopIndex();197: int nIndexClick = nTopIndex + pt.y / nItemHeight;198:199: // Get clicked item rect200: CRect rcItem;201: m_listBox.GetItemRect(nIndexClick, rcItem);202:203: if ( PtInRect(rcItem, pt) )204: {205: // Invert the check mark206: SetCheck(nIndexClick, !GetCheck(nIndexClick));207: ::InvalidateRect(m_listBox.m_hWnd, rcItem, FALSE);208:209: // Notify that selection has changed210: /*::SendMessage(this->GetParent().m_hWnd, WM_COMMAND,211: MAKELONG(::GetWindowLong(this->m_hWnd, GWL_ID), CBN_SELCHANGE),212: (LPARAM)this->m_hWnd);*/213:214: // 按照上面发送消息没有效果,直接刷新215: Invalidate(FALSE);216: }217:218: bHandled = FALSE;219: return 0;220: }221:222: LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)223: {224: // 什么都不做,让列表窗口在被选中了一个条目后仍然保持显示(不自动关闭)225: return 0;226: }227:228: LRESULT OnGetCurSel(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)229: {230: // Make the combobox always return -1 as the current selection. This231: // causes the lpDrawItemStruct->itemID in DrawItem() to be -1232: // when the always-visible-portion of the combo is drawn233: return -1;234: }235:236: LRESULT OnRButtonDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)237: {238: // If you want to select all/unselect all using the239: // right button, remove this ifdef. Personally, I don't really like it240: #if TRUE241: INT nCount = GetCount();242: INT nSelCount = 0;243:244: for (INT i = 0; i < nCount; i++)245: {246: if ( GetCheck(i) )247: nSelCount++;248: }249:250: SelectAll(nSelCount != nCount);251:252: // invalidate list box253: ::InvalidateRect(m_listBox.m_hWnd, 0, FALSE);254: #endif255:256: bHandled = FALSE;257: return 0;258: }259:260:261: //////////////////////////////////////////////////////////////////////////262: // Help263:264: //265: // This routine steps through all the items and builds266: // a string containing the checked items267: //268: void RecalcText()269: {270: if ( m_bTextUpdated )271: return;272:273: CString strText;274:275: int nCount = GetCount();276: for ( int i = 0; i < nCount; i++ )277: {278: if ( TRUE == GetCheck(i) )279: {280: CString strItem;281: GetLBText(i, strItem);282:283: if ( !strText.IsEmpty() )284: strText += _T(", ");285:286: strText += strItem;287: }288: }289:290: m_strTextAll = strText;291:292: m_bTextUpdated = TRUE;293: return;294: }295:296: protected:297: CContainedWindowT<CListBox> m_listBox;298: CString m_strTextAll; // 静态文本框显示的内容299: BOOL m_bTextUpdated;300: };


浙公网安备 33010602011771号