wince控件之自绘列表

来源:互联网 发布:某交友社区2千万数据 编辑:程序博客网 时间:2024/05/16 12:28

概述

上一篇讲述了如何在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 removedLVS_SHOWSELALWAYS  |LVS_EDITLABELS,LVS_REPORT         | // this styles are addedLVS_OWNERDRAWFIXED |LVS_NOCOLUMNHEADER |LVS_SINGLESEL,LVS_NOSCROLL));// Insert at least one column to the list controlVERIFY(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);    HBITMAPhbmOldBmp = 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);}

源文件下载地址:
http://download.csdn.net/detail/ryanzll/4551633
原创粉丝点击