Introduction
Download ownerDrawList.zip - 4.76 MB
Owner-draw listctrl with transparent background and customized items(with checkbox) image on wince
Background
I am developing a media player which should be running on windows embedded compact 7 system with customized hardware. Before this project, I have 0 experience of MFC and last time I did a windows based development was 10 years ago using C++builder. I am now actually a linux developer. Luckily with help of google/codeproject, I finished all things in 2 weeks. The purpose of this article is to help those developers which may have similar situation.
At first since I did not get hardware on hand, I have done all the development on windows desktop environment( windows 7). When I am trying to port code to wince platform, I found that wince did not support owner-draw listbox which I used to display playlist. Then I turn to use listctrl. I spent a day to convert my code from listbox to listctrl. Thanks again for codeproject.
Using the code
I create a new class MyListCtrl inherited from MFC ListCtrl and adding below features
- ability to set bitmap image for item icon
- ability to set bitmap image for item highlight
- ability to set bitmap image for item checkbox
- ability to be transparent to parent window
- ability to set item height
Below are corresponding interfaces for setting the image from resource ID
void SetItemHighlightImg(UINT id);
void SetItemIcon(UINT id);
void SetItemCheckedImg(UINT id);
void SetItemUnCheckedImg(UINT id);
void SetBk(CDC *pDC);
The core function for a owner-draw list ctrl is 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);
#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<int>::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);
}
}
To change the status of checkbox, we need to handle message ON_WM_LBUTTONDOWN and record mouse down position
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);
}
Points of Interest
The usage of MeasureItem to set item height did not work for listctrl. A simple way to set item height is to use a imagelist.
m_imageList.Create(24, 58, ILC_COLOR4,10,10 );
myList.SetImageList( &m_imageList, LVSIL_SMALL );
History
Keep a running update of any changes or improvements you've made here.