wince控件之自绘列表

概述

上一篇讲述了如何在wince上实现自绘按钮,这一篇将继续wince上的自绘控件--列表。效果如下图主要是依据当时的项目需要进行定制,无法像之前的自绘按钮那样具有通用性,不过如果了解了基本流程,修改起来应该没问题的!

  • 仅包含一列;
  • 在每列的前后显示图标,并且图标支持透明色;
  • 对于选中的行,加载并显示设定的底图;
  • 隐藏列表原有的滚动条,并重新自绘;
  • 未显示列表的头。


本自绘列表参考了codeproject上的文章"How to skin CListCtrl including scrollbars and column headers"(基于pc的实现)


主要步骤
  • 从CListCtrl派生子类,如CImageListCtrl;
  • 定制列表的样式,使之自绘,无列表头等;
  • 列表的自绘
  • 隐藏滚动条
  • 自绘滚动条

定制列表样式
重载虚函数virtual void PreSubclassWindow(),实现如下:
void CImageListCtrl::PreSubclassWindow()
{
	VERIFY(ModifyStyle(LVS_TYPEMASK       | // this styles are removed
		LVS_SHOWSELALWAYS  |
		LVS_EDITLABELS,
		LVS_REPORT         | // this styles are added
		LVS_OWNERDRAWFIXED |
		LVS_NOCOLUMNHEADER |
		LVS_SINGLESEL,		
		LVS_NOSCROLL));
	// Insert at least one column to the list control
	VERIFY(InsertColumn(0, _T("Column"), LVCFMT_LEFT) == 0);	
	
	//SetColumnWidth(0, LVSCW_AUTOSIZE_USEHEADER);
	CRect clientRect;
	GetClientRect(&clientRect);
	SetColumnWidth(0, clientRect.Width());

    CListCtrl::PreSubclassWindow();
}
由于定制的列表只有一列,所以这当中调用函数 SetColumnWidth 设置列的宽度为客户区的宽度。

列表的自绘
列表的自绘直接明了,重载virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct),其中参数lpDrawItemStruct,包含了当前所要绘制的列表子项(即列表的一行)的相关信息,包括绘制句柄 lpDrawItemStruct->hDC,列表子项的客户区 lpDrawItemStruct->rcItem
而列表子项的状态参数可以通过函数 GetItem( LV_ITEM & lvi)获得,一旦获取了上面这些参数,那么绘制当前的列表子项就是调用相关绘制函数了,以及如何调整图片,文字的显示位置。详细实现如下:
void CImageListCtrl::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;     // get all state flags
    GetItem(&lvi);

    BOOL isSelected = lvi.state & LVIS_SELECTED;

    CRect rcBounds;
    GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
    CString sLabel = GetItemText(nItem, 0);
	SetBkColor(RGB(192,192,192));
    PaintBk(pDC, rcItem);

    HBITMAP	hbmOldBmp = NULL;

    CDC bitmapDC;
    bitmapDC.CreateCompatibleDC(pDC);

    if (isSelected && m_selImg != NULL)
    {		
        hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(m_selImg);
        pDC->BitBlt(rcItem.left, rcItem.top, rcItem.Width(), rcItem.Height(), &bitmapDC,0,0,SRCCOPY);		
        bitmapDC.SelectObject(hbmOldBmp);
    }
    else
    {
	pDC->FillRect(rcItem,&CBrush(GetBkColor()));
    }

    if (m_ItemIcon != NULL)
    {        
        hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(m_ItemIcon);
        TransparentImage(pDC->m_hDC,rcItem.left+29, rcItem.top+12, 32, 32,
                bitmapDC.m_hDC,0, 0, 32, 32,RGB(0,0,0));
        bitmapDC.SelectObject(hbmOldBmp);
    }    

    CFont font;
    font.CreatePointFont(200, _T("Times New Roman"));
    pDC->SelectObject(&font);
    pDC->SetTextColor(m_textColor);
    pDC->DrawText(sLabel,rcItem, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
    font.DeleteObject();

    HBITMAP hbmNewBmp = NULL;
    if (isSelected)
    {
        if (m_checkedImg != NULL)
        {
            hbmNewBmp = m_checkedImg;			
        }
    }
    else if(m_uncheckedImg)
    {
        hbmNewBmp = m_uncheckedImg;
    }
    
    if (hbmNewBmp != NULL)
    {
        hbmOldBmp = (HBITMAP)bitmapDC.SelectObject(hbmNewBmp);
        TransparentImage(pDC->m_hDC,rcItem.right-50, rcItem.top+12, 32, 32,
                bitmapDC.m_hDC,0, 0, 32, 32,RGB(0,0,0));
        bitmapDC.SelectObject(hbmOldBmp);
    }	
}
此外,如果想绘制列表子项的边框,需要边界大小,可以通过如下方式得到:
CRect rcBounds;
GetItemRect(nItem, rcBounds, LVIR_BOUNDS);

隐藏滚动条
wince平台上想要隐藏(禁用)滚动条,没有直接的办法。因为列表的窗口包括显示列表子项的区域和显示滚动条的部分(我的猜测),所以间接的办法是:
  • 设置列表的窗口大小 = 原来的窗口大小 + 滚动条的大小
  • 设置列表的裁剪区域 = 原来的窗口大小
通过如上的方式,滚动条就因为被裁剪区域裁剪掉,而不会显示了。具体实现如下:
void CImageListCtrl::HideScrollBars()   
{    
	RECT ierect;   
	int cxvs, cyvs;   
	GetClientRect(&ierect); //Get client width and height   

	cxvs = GetSystemMetrics (SM_CXVSCROLL); //Get the system metrics - VERT   
	cyvs = GetSystemMetrics (SM_CYVSCROLL); //Get the system metrics - HORZ   

	//if(Which==SB_HORZ) cxvs=0; //Set VERT to zero when choosen HORZ   
	//if(Which==SB_VERT) cyvs=0; //Set HORZ to zero when choosen VERT   

	//Here we set the position of the window to the clientrect + the size of the scrollbars   
	SetWindowPos(NULL, ierect.left, ierect.top, ierect.right+cxvs, ierect.bottom+cyvs, SWP_NOMOVE | SWP_NOZORDER);   

	//Her we modify the rect so the right part is subbed from the rect.   
	ierect.bottom -= ierect.top;
	ierect.right -= ierect.left;

	//Just to be safe that the left/top corner is 0...   
	ierect.top = 0;   
	ierect.left = 0;   

	HRGN iehrgn = NULL; //This range is created base on which scrollbar that is going to be removed!   

	//The -2 is probably a border of some kind that we also need to remove. I could not find any good   
	//metrics that gave me an 2 as an answer. So insted we makes it static with -2.    
	iehrgn=CreateRectRgn (ierect.left, ierect.top, ierect.right-2, ierect.bottom-2);
	//After the range has been made we add it...   
	SetWindowRgn (iehrgn, TRUE);
}

自绘滚动条
剩下的事情,就是添加自绘的滚动条,响应滚动事件并更新滚动条的位置。其中比较麻烦的就是给滚动条设置合适的位置,具体方法为:
  • 得到列表的窗口位置并转换为屏幕坐标
  • 考虑到滚动条的宽度、标题栏的宽度等参数,用上一步的坐标计算出滚动条的位置并设置。
这当中第二步的位置计算不是绝对的,根据绘制的需要和经验可以进行相应的调整。具体实现参见函数CImageListCtrl::PositionScrollBars()
至于滚动条的绘制和滚动都封装在类CSkinHorizontalScrollbarCSkinVerticleScrollbar中,并不难理解。

滚动条自动滚动的问题
前面的文章-- CListCtrl自动滚动的问题 曾经提到列表会自动滚动,你可以选择按照文章中的办法禁用掉,或者在函数void CImageListCtrl::OnTimer(UINT nIDEvent)中处理对应的nIDEvent事件,然后调用类CSkinHorizontalScrollbarCSkinVerticleScrollbar的函数UpdateThumbPosition()来更新滚动条的位置。

用例
在对话框类中定义成员变量CImageListCtrl m_imageListCtrl并通过DDX_Control(pDX, IDC_LIST2, m_imageListCtrl)控件与变量绑定,然后在对话框类的初始化函数中
m_imageListCtrl.LoadVScrollImages(IDB_VERTICLE_SCROLLBAR_BOTTOM,IDB_VERTICLE_SCROLLBAR_DOWNARROW,IDB_VERTICLE_SCROLLBAR_SPAN,
		IDB_VERTICLE_SCROLLBAR_THUMB,IDB_VERTICLE_SCROLLBAR_TOP,IDB_VERTICLE_SCROLLBAR_UPARROW);
	m_imageListCtrl.LoadHScrollImages(IDB_HORIZONTAL_SCROLLBAR_LEFTARROW,IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW,
		IDB_HORIZONTAL_SCROLLBAR_SPAN,IDB_HORIZONTAL_SCROLLBAR_THUMB);
	m_imageListCtrl.InitCtrl();
	//m_imageListCtrl.SetImageList(   &m_imageList,   LVSIL_SMALL   );
	m_imageListCtrl.SetItemCheckedImg(IDB_LISTCTRL_CHECKED_BITMAP);
	m_imageListCtrl.SetItemSelectedImg(IDB_LISTCTRL_ITEM_SEL_BITMAP);
	m_imageListCtrl.SetItemIcon(IDB_LISTCTRL_ICON_BITMAP);
	//m_imageListCtrl.SetItemUnCheckedImg(IDB_LISTCTRL_UNCHECK_BITMAP);
	for (int n = 0; n < 8; n++)
	{
		CString str;
		str.Format(_T("Item %d"), n);
		m_imageListCtrl.InsertItem(n, str);
	}

源文件下载地址:
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值