在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);