WTL 自绘ComboBox带CheckBox

效果图:

1

原帖: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 Reserved
  4: //
  5: // FileName:      CheckComboBox.h  
  6: // Author:        yinxf
  7: // Date:          2012/2012/4
  8: // Description:   带checkbox的ComboBox,自绘
  9: //                要将ComboBox的Tyep设置为Drop List
 10: //                               OwnerDraw设置为Variable
 11: //                               Has Strings设置为True
 12: // Function List:  
 13: // History:              
 14: //          <author>        <time>           <desc>        
 15: //            yinxf          2010/07/13        Build
 16: //
 17: //*************************************************************************
 18: #pragma once
 19: 
 20: class CCheckComboBox
 21:     : 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 updating
 39:        m_bTextUpdated = FALSE;
 40: 
 41:        // Redraw the window       
 42:        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 all
 54:     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 style        
 87:             ATLASSERT(style & CBS_DROPDOWNLIST);        
 88:             // Make sure to use the CBS_OWNERDRAWVARIABLE style        
 89:             ATLASSERT(style & CBS_OWNERDRAWVARIABLE);        
 90:             // Use default strings. We need the itemdata to store checkmarks       
 91:             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:         else 
111:         {
112:             BOOL bCheck = GetCheck(lpdis->itemID);
113:             GetLBText(lpdis->itemID, strText);
114: 
115:             // 绘制checkbox
116:             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:         else
135:         {
136:             SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW));
137:             SetTextColor(lpdis->hDC, GetSysColor(COLOR_WINDOWTEXT));
138:         }
139: 
140:         // Erase and draw       
141:         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 updated
165:         RecalcText();
166: 
167:         if (NULL == lpszText)
168:             return 0;
169: 
170:         // Copy the 'fake' window text
171:         _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 updated
181:         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/uncheck
195:         int nItemHeight = GetItemHeight(0);
196:         int nTopIndex = GetTopIndex();
197:         int nIndexClick = nTopIndex + pt.y / nItemHeight;
198: 
199:         // Get clicked item rect
200:         CRect rcItem;
201:         m_listBox.GetItemRect(nIndexClick, rcItem);
202: 
203:         if ( PtInRect(rcItem, pt) )
204:         {
205:              // Invert the check mark
206:             SetCheck(nIndexClick, !GetCheck(nIndexClick));
207:             ::InvalidateRect(m_listBox.m_hWnd, rcItem, FALSE);
208:             
209:             // Notify that selection has changed
210:             /*::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. This
231:         // causes the lpDrawItemStruct->itemID in DrawItem() to be -1
232:         // when the always-visible-portion of the combo is drawn
233:         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 the
239:         // right button, remove this ifdef. Personally, I don't really like it
240: #if TRUE
241:         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 box
253:         ::InvalidateRect(m_listBox.m_hWnd, 0, FALSE);
254: #endif
255: 
256:         bHandled = FALSE;
257:         return 0;
258:     }
259: 
260: 
261:     //////////////////////////////////////////////////////////////////////////
262:     // Help
263: 
264:     //
265:     // This routine steps through all the items and builds    
266:     // a string containing the checked items    
267:     //
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: };
posted @ 2012-07-06 08:26  HoneyComb  阅读(3808)  评论(12编辑  收藏  举报