我建了个CListView的对象,这个视图中的ListCtrl (Report型)中用来显示数据,所显示的数据是实时变化的,并且,各个显示数值的SubItem要求有背景色,且背景色并不总占满整个SubItem的区域, 而是根据数值的大小,背景色的长度会变化,而且长度在不同的范围内,颜色也不一样.举例说明:以下为CListView中的ListCtrl
------------------------------------------
|    ParaHeader   |      ValueHeader     |
------------------------------------------
|    Para1        |      Value1          |
------------------------------------------
|    Para2        |      Value2          |
------------------------------------------如图所示,Value1, Value2为两个不断变化的值, 要求这两个值所在的SubItem有背景色,且背景色根据Value1,Value2的大小变化,相应变长或缩短(从左到右)请问该如何实现啊? 

解决方案 »

  1.   

    接收 NM_CUSTOMDRAW 消息,自绘制
      

  2.   

    void CEditorList::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 
    {
    // This function is called by the control in different 
    // stages during the control drawing process.
    NMLVCUSTOMDRAW *pCD = (NMLVCUSTOMDRAW*)pNMHDR;
    // By default set the return value to do the default behavior.
    *pResult = 0; switch( pCD->nmcd.dwDrawStage )
    {
    case CDDS_ITEMPREPAINT: // Stage three (called for each subitem of the focused item)
    {
    *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT | CDRF_NEWFONT;
    }
    break;
    //Here we draw the expanded and collapsed icons and the column
    case  CDDS_POSTPAINT :
    {
    *pResult = CDRF_DODEFAULT; 
    }
    break;
    case  CDDS_PREPAINT:  // First stage (for the whole control)
    {
    // Tell the control we want to receive drawing messages  
    // for drawing items.
    *pResult = CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT;
    }
    break;
    case CDDS_ITEMPREPAINT | CDDS_SUBITEM: // Stage three (called for each subitem of the focused item)
    {
    // We don't want to draw anything here, but we need to respond 
    // of DODEFAULT will be the next stage.
    // Tell the control we want to handle drawing after the subitem 
    // is drawn.
    *pResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT;
    }
    break;
    case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: // Stage four (called for each subitem of the focused item)
    {
    // We do the drawing here (well maybe).
    // This is actually after the control has done its drawing
    // on the subitem.  Since drawing a cell is near instantaneous
    // the user won't notice.
    // If this subitem is the subitem with the current focus,
    // draw it.  Otherwise let the control draw it.  
    CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
    ASSERT(NULL != pHeader);
    // We have to take into account the possibility that the 
    // columns may be reordered.
    int nItem = pCD->nmcd.dwItemSpec;
    int nSubItem = Header_OrderToIndex(pHeader->m_hWnd,  pCD->iSubItem); // Get drawing dc
    CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
    if (nSubItem == 0)
    {
    CRect rect;
    GetSubItemRectEx(nItem, 0, rect);
    pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH);
    CImageList * pImage = GetImageList(LVSIL_SMALL);
    POINT pt;
    pt.x = rect.left; pt.y = rect.top;
    int nState = GetItemState(nItem, 0xFFFF);
    pImage->Draw(pDC, ((nState&LVIS_SELECTED)!=0), pt, ILD_TRANSPARENT);
    *pResult = CDRF_SKIPDEFAULT;
    return;
    }
    else if (m_nCurSubItem == nSubItem
    && (pCD->nmcd.uItemState & CDIS_FOCUS)
    && nSubItem >= IDX_FM_FAMILY)
    {
    /*
    int nState = GetItemState(nItem, 0xFFFF);
    SetItemState(nItem, nState&(~(LVIS_SELECTED|LVIS_FOCUSED)), 0xFFFF);
    CRect rect;
    GetItemRect(nItem, rect, LVIR_BOUNDS);
    InvalidateRect(rect, TRUE);
    */
    DrawFocusSubItem(pDC, nItem, nSubItem);
    *pResult = CDRF_SKIPDEFAULT;
    return;
    }
    else
    {
    *pResult = CDRF_DODEFAULT;
    return;
    }
    }
    break;
    default: // Stage two handled here. (called for each item)
    {
    *pResult = CDRF_NOTIFYSUBITEMDRAW;
    break;
    }
    }
      

  3.   

    这是我以前项目里的代码,这个ListCtrl功能比你的要求复杂多了,我处理了case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:
    觉得足够解决你的问题了。
    这样映射消息
    CPP
    ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
    H
    afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
    别放在Class Wizard的//}}AFX_MSG_MAP里面,否则VC会冲掉它
      

  4.   

    dandycheung(珠穆朗玛) ,sjhunter:
      谢谢两位!
      不过能不能说得在具体点.  NM_CUSTOMDRAW 是个自定义的消息吧? 在Class Wizard中好象没看到.sjhunter:
      我的情况可能和你有一点点不同吧,我用的是CListView中的ListCtrl.
       你的代码中能实现SubItem文字的背景色占满一半SubItem的区域那样的情况吗?  我现在能做到的是实现背景色占满SubItem一半区域, 但文字一上去就冲掉, 我考虑的是有没有什么异或的涂色或写文字的方法, 但还没试出来.
      
      这个问题弄了我快1天的时间了,郁闷ING!
      

  5.   

    我现在有了些进展,
    取得某个SubItem的CRect,
    然后根据取得的CRect,定制自己的一个要填充背景色的CRect,
    填色, 然后设置Text Mode为透明, 将文字显示出来,这样就能达到我要的那种效果.但现在还有个问题,原来CListCtrl中有很多数据,一旦进行上面的操作进行填色后,原来的数据全消失了, 也不知道那个地方的问题,可能导致了重画, 正解决中.......
      

  6.   

    我那段代码可以呀,我当初做的ListCtrl控件可麻烦了,你的这个要求用这段代码可以实现。
    首先:NM_CUSTOMDRAW是不能在Class Wizard中添加的,但是它不是自定义消息,你可以查一查MSDN
    其次:你只要在case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM:里做就可以了,首先添颜色,然后SetBKMode,再GetItemText,然后DrawText
    至于你是ListView,你可能需要SubclassWindow一下,将它的ListCtrl挂在你自己的ListCtrl下
      

  7.   

    还可以派生CListView类,重载DrawItem(),这个也很方便,如果有兴趣可以告诉我你的mail,我给你邮过去我的过去的类似代码和一片文章。
      

  8.   

    mousefj:
       你说的方法我找到了, 重载一个CListView类,在DrawItem()处理. 在重载的DrawItem()中加一段代码,的确可以达到我要的效果,不过好像很难从外部控制.即,DrawItem我们可以主动调用它吗? 好像不行啊,它好像是被框架调用的.那样的话,我要指定的一些参数好像很难传进去啊?(如我要指定画的背景的颜色,宽度等)
       如果背景颜色的设置只是一遍就完的话,好像DrawItem()中加段代码就可以了,不过我的背景色要根据不同情况人为去设的呀!?sjhunter:
      还没来得及非常仔细地看你的CODE,先想问一下,你的那种做法,能通过传参数什么的,人为不断地去指定颜色吗?    对关注本帖者,非常感谢! 愿各位新的一年中,快乐平安!
      

  9.   

    DrawItem是在ListView刷新是调用现实每一个Item。我建议在新的派生类中加入一个CList,用于保存每个Item的颜色信息,定义如下:
    CList<ITEM_APPEND_ATTR,ITEM_APPEND_ATTR&> ItemAppendAttr;   //save all itemColor
    其中ITEM_APPEND_ATTR的定义如下:
    typedef struct Item_Append_Attr
    {
    COLORREF BKColor;
    char ItemLever;
    BOOL IsLeverFirst;
    BOOL IsOpen;
    }ITEM_APPEND_ATTR;
    然后添加函数BOOL SetItemColor(int vnItemIndex,COLORREF vdwItemColor);
    BOOL CSwithListCtrl::SetItemColor(int vnItemIndex, COLORREF vdwItemColor)
    {
    if(vnItemIndex>=GetItemCount())
    return FALSE;
    POSITION pos=ItemAppendAttr.FindIndex(vnItemIndex);
    ITEM_APPEND_ATTR m_ItemAppendAttr;
    m_ItemAppendAttr=ItemAppendAttr.GetAt(pos);
    m_ItemAppendAttr.BKColor=vdwItemColor;
    ItemAppendAttr.SetAt(pos,m_ItemAppendAttr);
    return TRUE;
    }
    这个函数外部可调用。
    在DrawItem()中设置文本的地方加入以下代码即可实现
    CRect rcClient, rcRow = rcItem;
    GetClientRect(&rcClient);
    rcRow.right = rcClient.right;
    COLORREF m_colorItem;
    if(nItem==m_nCurItemIndex)
        m_colorItem=RGB(88,167,169);
    else
        m_colorItem=ItemAppendAttr.GetAt(ItemAppendAttr.FindIndex(nItem)).BKColor;
    pDC->FillRect(rcItem, &CBrush(m_colorItem));//;RGB(120,255,255)我已实现了非常好用
      

  10.   

    mousefj:
        非常感谢你的指导! 按照你的思路, 我已经搞定了! sjhunter: 
       也非常谢谢你, 等有了喘息的机会,我也试试你说的方法.其余各位:
       多谢各位关注, 大家一起提高!