自定义控件开发经验分享

在Windows Embedded Compact 7系统上开发媒体播放器时,遇到了一些挑战。由于之前没有使用MFC的经验,且上一次进行Windows平台开发已经是10年前使用C++ Builder的时候了。幸运的是,借助于Google和CodeProject的帮助,仅用了两周时间就完成了所有开发工作。本文的目的是帮助那些可能遇到类似情况的开发者。

最初,由于没有硬件在手,所有的开发工作都是在Windows桌面环境(Windows 7)上完成的。当尝试将代码移植到WinCE平台时,发现WinCE不支持之前用于显示播放列表的自定义绘制列表框(owner-draw listbox)。因此,转向使用ListCtrl。花了一天时间将代码从listbox转换为listctrl。再次感谢CodeProject的帮助。

使用代码

创建了一个名为MyListCtrl的新类,继承自MFC的ListCtrl,并添加了以下功能:

  • 为项目图标设置位图图像的能力
  • 为项目高亮设置位图图像的能力
  • 为项目复选框设置位图图像的能力
  • 对父窗口透明
  • 设置项目高度

以下是设置资源ID图像的相应接口:

void SetItemHighlightImg(UINT id); void SetItemIcon(UINT id); void SetItemCheckedImg(UINT id); void SetItemUnCheckedImg(UINT id); void SetBk(CDC *pDC);

自定义绘制ListCtrl的核心函数是DrawItem:

void MyListCtrl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ) { ASSERT(::IsWindow(m_hWnd)); ASSERT(lpDrawItemStruct != 0); CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rcItem(lpDrawItemStruct->rcItem); int nItem = lpDrawItemStruct->itemID; LV_ITEM lvi; lvi.mask = LVIF_STATE; lvi.iItem = nItem; lvi.iSubItem = 0; lvi.stateMask = 0xFFFF; // 获取所有状态标志 GetItem(&lvi); BOOL bHighlight = lvi.state & LVIS_FOCUSED; CRect rcBounds; GetItemRect(nItem, rcBounds, LVIR_BOUNDS); CString sLabel = GetItemText(nItem, 0); PaintBk(pDC, rcItem); HBITMAP hbmOldBmp = NULL; CDC bitmapDC; bitmapDC.CreateCompatibleDC(pDC); if (bHighlight) { hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(m_SelImg); pDC->BitBlt(rcItem.left, rcItem.top, rcItem.Width(), rcItem.Height(), &bitmapDC, 0, 0, SRCCOPY); bitmapDC.SelectObject(hbmOldBmp); } ResConfigRec iRec = SkinConfigMgr::GetResConfig(IDB_IMG_ITEM_ICON); hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(m_ItemIcon); pDC->BitBlt(rcItem.left + iRec.resX, rcItem.top + iRec.resY, iRec.resW, iRec.resH, &bitmapDC, 0, 0, SRCCOPY); bitmapDC.SelectObject(hbmOldBmp); // NOTE: 请用自己的逻辑替换下面的代码块 // 在实现中,使用std::vector来存储每个项目的复选框状态 // std::vector* m_pStatusMap #if 0 iRec = SkinConfigMgr::GetResConfig(IDB_IMG_ITEM_UC); int checked = m_pStatusMap->at(nItem); if ((rcItem.left + iRec.resX < bMouseDownPos.x && bMouseDownPos.x < rcItem.left + iRec.resX + iRec.resW) && (rcItem.top + iRec.resY < bMouseDownPos.y && bMouseDownPos.y < rcItem.top + iRec.resY + iRec.resH)) { std::vector::iterator iter = m_pStatusMap->begin() + nItem; if (checked == 1) { *iter = 0; } else { *iter = 1; } checked = *iter; bMouseDownPos.x = 0; bMouseDownPos.y = 0; } #endif CFont font; font.CreatePointFont(200, _T("Times New Roman")); pDC->SelectObject(&font); pDC->SetTextColor(RGB(255, 255, 255)); pDC->DrawText(sLabel, rcItem, DT_CENTER); if (checked == 1) { hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(m_CbChecked); pDC->BitBlt(rcItem.left + iRec.resX, rcItem.top + iRec.resY, iRec.resW, iRec.resH, &bitmapDC, 0, 0, SRCCOPY); bitmapDC.SelectObject(hbmOldBmp); } else { hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(m_CbUnChecked); pDC->BitBlt(rcItem.left + iRec.resX, rcItem.top + iRec.resY, iRec.resW, iRec.resH, &bitmapDC, 0, 0, SRCCOPY); bitmapDC.SelectObject(hbmOldBmp); } }

要改变复选框的状态,需要处理消息ON_WM_LBUTTONDOWN并记录鼠标按下的位置:

CPoint bMouseDownPos; void MyListCtrl::OnLButtonDown( UINT nFlags, CPoint point ) { bMouseDownPos = point; Default(); int iPos = GetNextItem(-1, LVNI_ALL | LVNI_SELECTED); CRect rcItem; GetItemRect(iPos, &rcItem, LVIR_BOUNDS); // NOTE: 用自己的逻辑替换这段代码 // ResConfigRec iRec = SkinConfigMgr::GetResConfig(IDB_IMG_ITEM_UC); // if ((rcItem.left + iRec.resX < bMouseDownPos.x && bMouseDownPos.x < rcItem.left + iRec.resX + iRec.resW) // && (rcItem.top + iRec.resY < bMouseDownPos.y && bMouseDownPos.y < rcItem.top + iRec.resY + iRec.resH)) // { // InvalidateRect(&rcItem); // } }

注意事项

m_imageList.Create(24, 58, ILC_COLOR4, 10, 10); myList.SetImageList(&m_imageList, LVSIL_SMALL);
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485