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: };

你可能感兴趣的:(checkbox)