www.pudn.com > multichecklistbox_demo.zip > MultiCheckListBox.cpp
// MultiCheckListBox.cpp : implementation file // #include "stdafx.h" #include "CheckBoxTest.h" #include "MultiCheckListBox.h" #include#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif class _AFX_CHECKLIST_STATE : public CNoTrackObject { public: _AFX_CHECKLIST_STATE(); virtual ~_AFX_CHECKLIST_STATE(); HBITMAP m_hbitmapCheck; CSize m_sizeCheck; }; _AFX_CHECKLIST_STATE::_AFX_CHECKLIST_STATE() { CBitmap bitmap; if (afxData.bWin4 || AfxGetCtl3dState()->m_pfnSubclassDlgEx != NULL) VERIFY(bitmap.LoadBitmap(AFX_IDB_CHECKLISTBOX_95)); else VERIFY(bitmap.LoadBitmap(AFX_IDB_CHECKLISTBOX_NT)); BITMAP bm; bitmap.GetObject(sizeof (BITMAP), &bm); m_sizeCheck.cx = bm.bmWidth / 3; m_sizeCheck.cy = bm.bmHeight; m_hbitmapCheck = (HBITMAP)bitmap.Detach(); } _AFX_CHECKLIST_STATE::~_AFX_CHECKLIST_STATE() { if (m_hbitmapCheck != NULL) ::DeleteObject(m_hbitmapCheck); } EXTERN_PROCESS_LOCAL(_AFX_CHECKLIST_STATE, _afxChecklistState) ///////////////////////////////////////////////////////////////////////////// // AFX_CHECK_DATA struct AFX_CHECK_DATA { public: int m_nCheck; BOOL m_bEnabled; DWORD m_dwUserData; int m_nCheck2; AFX_CHECK_DATA() { m_nCheck = 0; m_bEnabled = TRUE; m_dwUserData = 0; m_nCheck2 = 0; }; }; ///////////////////////////////////////////////////////////////////////////// // CMultiCheckListBox CMultiCheckListBox::CMultiCheckListBox() { } CMultiCheckListBox::~CMultiCheckListBox() { } BEGIN_MESSAGE_MAP(CMultiCheckListBox, CCheckListBox) //{{AFX_MSG_MAP(CMultiCheckListBox) ON_WM_MEASUREITEM() ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMultiCheckListBox message handlers void CMultiCheckListBox::PreDrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { _AFX_CHECKLIST_STATE* pChecklistState = _afxChecklistState; if ((((LONG)lpDrawItemStruct->itemID) >= 0) && ((lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) != 0)) { int cyItem = GetItemHeight(lpDrawItemStruct->itemID); CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); COLORREF newBkColor = GetSysColor(COLOR_WINDOW); BOOL fDisabled = !IsWindowEnabled() || !IsEnabled(lpDrawItemStruct->itemID); if ((lpDrawItemStruct->itemState & ODS_SELECTED) && !fDisabled) newBkColor = GetSysColor(COLOR_HIGHLIGHT); COLORREF oldBkColor = pDC->SetBkColor(newBkColor); int nCheck; CDC bitmapDC; HBITMAP hOldBitmap; CRect rectCheck; CRect rectItem; CRect rectCheckBox; if (bitmapDC.CreateCompatibleDC(pDC)) { nCheck = GetCheck(lpDrawItemStruct->itemID); hOldBitmap = (HBITMAP)::SelectObject(bitmapDC.m_hDC, pChecklistState->m_hbitmapCheck); rectCheck = lpDrawItemStruct->rcItem; rectCheck.left += 1; rectCheck.top += 1 + max(0, (cyItem - pChecklistState->m_sizeCheck.cy) / 2); rectCheck.right = rectCheck.left + pChecklistState->m_sizeCheck.cx; rectCheck.bottom = rectCheck.top + pChecklistState->m_sizeCheck.cy; rectItem = lpDrawItemStruct->rcItem; rectItem.right = rectItem.left + pChecklistState->m_sizeCheck.cx + 2; rectItem.right += pChecklistState->m_sizeCheck.cx + 2; rectCheckBox = OnGetCheckPosition(rectItem, rectCheck); ASSERT(rectCheck.IntersectRect(rectItem, rectCheckBox)); ASSERT((rectCheck == rectCheckBox) && (rectCheckBox.Size() == pChecklistState->m_sizeCheck)); CBrush first_brush(newBkColor); pDC->FillRect(rectItem, &first_brush); pDC->BitBlt(rectCheckBox.left, rectCheckBox.top, pChecklistState->m_sizeCheck.cx, pChecklistState->m_sizeCheck.cy, &bitmapDC, pChecklistState->m_sizeCheck.cx * nCheck, 0, SRCCOPY); ::SelectObject(bitmapDC.m_hDC, hOldBitmap); } pDC->SetBkColor(oldBkColor); } if (lpDrawItemStruct->itemData != 0) { AFX_CHECK_DATA* pState = (AFX_CHECK_DATA*)lpDrawItemStruct->itemData; lpDrawItemStruct->itemData = pState->m_dwUserData; } lpDrawItemStruct->rcItem.left = lpDrawItemStruct->rcItem.left + pChecklistState->m_sizeCheck.cx + 2; CCheckListBox::PreDrawItem(lpDrawItemStruct); } BOOL CMultiCheckListBox::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult) { switch (message) { case WM_DRAWITEM: ASSERT(pLResult == NULL); // no return value expected PreDrawItem((LPDRAWITEMSTRUCT)lParam); break; default: return CCheckListBox::OnChildNotify(message, wParam, lParam, pLResult); } return TRUE; } PROCESS_LOCAL(_AFX_CHECKLIST_STATE, _afxChecklistState) void CMultiCheckListBox::SetCheck(int nIndex, int nCheck) { ASSERT(::IsWindow(m_hWnd)); if (nCheck == 2) { if (m_nStyle == BS_CHECKBOX || m_nStyle == BS_AUTOCHECKBOX) return; } LRESULT lResult = DefWindowProc(LB_GETITEMDATA, nIndex, 0); if (lResult != LB_ERR) { AFX_CHECK_DATA* pState = (AFX_CHECK_DATA*)lResult; if (pState == NULL) pState = new AFX_CHECK_DATA; pState->m_nCheck2 = nCheck; VERIFY(DefWindowProc(LB_SETITEMDATA, nIndex, (LPARAM)pState) != LB_ERR); InvalidateCheck(nIndex); } } int CMultiCheckListBox::GetCheck(int nIndex) { ASSERT(::IsWindow(m_hWnd)); LRESULT lResult = DefWindowProc(LB_GETITEMDATA, nIndex, 0); if (lResult != LB_ERR) { AFX_CHECK_DATA* pState = (AFX_CHECK_DATA*)lResult; if (pState != NULL) return pState->m_nCheck2; // check in first checkbox } return 0; // The default } void CMultiCheckListBox::InvalidateCheck(int nIndex) { CRect rect; _AFX_CHECKLIST_STATE* pChecklistState = _afxChecklistState; GetItemRect(nIndex, rect); rect.right = rect.left + pChecklistState->m_sizeCheck.cx + 2; rect.right = rect.left + pChecklistState->m_sizeCheck.cx + 2; InvalidateRect(rect, FALSE); } void CMultiCheckListBox::SetMultiCheck(int nIndex, int nCheck) { ASSERT(::IsWindow(m_hWnd)); if (nCheck == 2) { if (m_nStyle == BS_CHECKBOX || m_nStyle == BS_AUTOCHECKBOX) return; } LRESULT lResult = DefWindowProc(LB_GETITEMDATA, nIndex, 0); if (lResult != LB_ERR) { AFX_CHECK_DATA* pState = (AFX_CHECK_DATA*)lResult; if (pState == NULL) pState = new AFX_CHECK_DATA; pState->m_nCheck = nCheck; VERIFY(DefWindowProc(LB_SETITEMDATA, nIndex, (LPARAM)pState) != LB_ERR); InvalidateMultiCheck(nIndex); } } int CMultiCheckListBox::GetMultiCheck(int nIndex) { ASSERT(::IsWindow(m_hWnd)); LRESULT lResult = DefWindowProc(LB_GETITEMDATA, nIndex, 0); if (lResult != LB_ERR) { AFX_CHECK_DATA* pState = (AFX_CHECK_DATA*)lResult; if (pState != NULL) return pState->m_nCheck; } return 0; // The default } void CMultiCheckListBox::InvalidateMultiCheck(int nIndex) { CRect rect; _AFX_CHECKLIST_STATE* pChecklistState = _afxChecklistState; GetItemRect(nIndex, rect); rect.left += pChecklistState->m_sizeCheck.cx + 2; InvalidateRect(rect, FALSE); } int CMultiCheckListBox::CheckFromPoint(CPoint point, BOOL& bInCheck) { // assume did not hit anything bInCheck = FALSE; int nIndex = -1; _AFX_CHECKLIST_STATE* pChecklistState = _afxChecklistState; if ((GetStyle() & (LBS_OWNERDRAWFIXED|LBS_MULTICOLUMN)) == LBS_OWNERDRAWFIXED) { // optimized case for ownerdraw fixed, single column int cyItem = GetItemHeight(0); if (point.y < cyItem * GetCount()) { nIndex = GetTopIndex() + point.y / cyItem; if (point.x < pChecklistState->m_sizeCheck.cx + 2) ++bInCheck; } } else { // general case for ownerdraw variable or multiple column for (int i = GetTopIndex(); i < GetCount(); i++) { CRect itemRect; GetItemRect(i, &itemRect); if (itemRect.PtInRect(point)) { nIndex = i; if (point.x < itemRect.left + pChecklistState->m_sizeCheck.cx + 2) ++bInCheck; break; } } } return nIndex; } int CMultiCheckListBox::InMultiCheck(CPoint point, BOOL& bInMultiCheck) { // assume did not hit anything bInMultiCheck = FALSE; int nIndex = -1; _AFX_CHECKLIST_STATE* pChecklistState = _afxChecklistState; if ((GetStyle() & (LBS_OWNERDRAWFIXED|LBS_MULTICOLUMN)) == LBS_OWNERDRAWFIXED) { // optimized case for ownerdraw fixed, single column int cyItem = GetItemHeight(0); if (point.y < cyItem * GetCount()) { nIndex = GetTopIndex() + point.y / cyItem; if (point.x < pChecklistState->m_sizeCheck.cx + 2 + (pChecklistState->m_sizeCheck.cx + 2)) ++bInMultiCheck; } } else { // general case for ownerdraw variable or multiple column for (int i = GetTopIndex(); i < GetCount(); i++) { CRect itemRect; GetItemRect(i, &itemRect); if (itemRect.PtInRect(point)) { nIndex = i; if (point.x < itemRect.left + pChecklistState->m_sizeCheck.cx + 2) ++bInMultiCheck; break; } } } return nIndex; } void CMultiCheckListBox::OnLButtonDown(UINT nFlags, CPoint point) { SetFocus(); // determine where the click is BOOL bInCheck; BOOL bInMultiCheck; int nIndex = CheckFromPoint(point, bInCheck); int n2Index = InMultiCheck(point, bInMultiCheck); // if the item is disabled, then eat the click if (!IsEnabled(nIndex)) return; if (m_nStyle != BS_CHECKBOX && m_nStyle != BS_3STATE) { // toggle check mark automatically if check mark was hit if (bInCheck) { CWnd* pParent = GetParent(); ASSERT_VALID(pParent); int nModulo = (m_nStyle == BS_AUTO3STATE) ? 3 : 2; int nCheck = GetCheck(nIndex); nCheck = (nCheck == nModulo) ? nCheck - 1 : nCheck; SetCheck(nIndex, (nCheck + 1) % nModulo); InvalidateCheck(nIndex); CListBox::OnLButtonDown(nFlags, point); // Inform of check pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CLBN_CHKCHANGE), (LPARAM)m_hWnd); return; } if (bInMultiCheck) { CWnd* pParent = GetParent(); ASSERT_VALID(pParent); int nModulo = (m_nStyle == BS_AUTO3STATE) ? 3 : 2; int nCheck = GetMultiCheck(nIndex); nCheck = (nCheck == nModulo) ? nCheck - 1 : nCheck; SetMultiCheck(nIndex, (nCheck + 1) % nModulo); CListBox::OnLButtonDown(nFlags, point); // Inform of check pParent->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), CLBN_CHKCHANGE), (LPARAM)m_hWnd); return; } } // do default listbox selection logic CListBox::OnLButtonDown(nFlags, point); }