CListCtrl 的自绘

来源:互联网 发布:做seo怎么样 编辑:程序博客网 时间:2024/06/05 05:50

自行绘制 ClistCtrl 的列表项,可以利用列表框的 NM_CUSTOMDRAW 消息,该消息由控件向它的父窗口发送,告诉父窗口它的绘图操作。

    如果绘图操作不需要父窗口参与,可以使用该控件的 ON_NOTIFY_REFLECT 宏处理它的 NM_CUSTOMDRAW 消息。

    它的处理函数的参数中包含 NMHDR,在 CUSTOMDRAW 的通知下 NMHDR 可以被转换成为 NMLVCUSTOMDRAW 结构,该结构包含了列表控件中需要自绘区域的全部信息:

typedef struct tagNMLVCUSTOMDRAW 

    NMCUSTOMDRAW   nmcd;                // 包含客户自绘控件信息的结构 
    COLORREF             clrText;              // 列表视图显示文字的颜色 
    COLORREF             clrTextBk;          // 列表视图显示文字的背景色 
} NMLVCUSTOMDRAW, *LPNMLVCUSTOMDRAW;

 

typedef struct tagNMLVCUSTOMDRAW
{
    NMCUSTOMDRAW nmcd;
    COLORREF clrText;                     //列表视图显示文字的颜色
    COLORREF clrTextBk;                 //列表视图显示文字的背景色 
#if (_WIN32_IE >= 0x0400)
    int iSubItem;                             //子项编号
#endif
#if (_WIN32_WINNT >= 0x501)
    DWORD dwItemType;               //

    // Item custom draw
    COLORREF clrFace;                   //
    int iIconEffect;
    int iIconPhase;
    int iPartId;
    int iStateId;

    // Group Custom Draw
    RECT rcText;
    UINT uAlign;      // Alignment. Use LVGA_HEADER_CENTER, LVGA_HEADER_RIGHT, LVGA_HEADER_LEFT
#endif
} NMLVCUSTOMDRAW, *LPNMLVCUSTOMDRAW;

 

NMCUSTOMDRAW 结构定义如下:

typedef struct tagNMCUSTOMDRAWINFO 

    NMHDR     hdr;                     // 含有通知信息的 NMHDR 结构 
    DWORD   dwDrawStage;       // 目前绘制的步骤 
    HDC         hdc;                     // 设备上下文句柄 
    RECT        rc;                       // 绘制的区域 
    DWORD    dwItemSpec;        // 绘制项的说明 
    UINT        uItemState;          // 当前项的状态 
    LPARAM   lItemlParam           // 应用程序定义的数据 
} NMCUSTOMDRAW, FAR * LPNMCUSTOMDRAW;

    NMLVCUSTOMDRAW.nmcd.dwDrawStage 字段,它主要包含以下几个枚举值:

  • CDDS_PREPAINT:表示在绘画前阶段。
  • CDDS_ITEMPREPAINT:表示在列表项的绘画前阶段。
  • CDDS_SUBITEM:表示绘制子项。
  • CDDS_ITEM:表示要绘制项的信息已经可用。

    自绘时,可以通过处理以上几个绘画阶段的通知来实现。

 

例子详细步骤:

  1. 利用 MFC AppWizard( exe ) 创建一个新工程 TestCListCtrl。在向导的第 ( 1 ) 步选择单文档模式,而后使用默认值来创建,最后获得一个支持文档视图的应用程序。
  2. 在 ClassView 中添加新的类 CCoolListCtrl,类型为 MFC Class,基类为CListCtrl。获得两个新文件 CCoolListCtrl.cpp 和 CCoolListCtrl.h。
  3. 在类 CCoolListCtrl 中添加成员变量 m_imagelist 用于存储图像列表。
  4. 添加 NM_CUSTOMDRAW 消息的处理函数 OnCustomDraw。

    CCoolListCtrl.h 文件中: 
    afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);//消息函数声明

    CCoolListCtrl.cpp 文件中: 
    // 消息映射宏中添加:ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)

    BEGIN_MESSAGE_MAP(CCoolListCtrl, CListCtrl) 
        //{{AFX_MSG_MAP(CCoolListCtrl) 
        ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) 
        //}}AFX_MSG_MAP 
    END_MESSAGE_MAP()

    // 消息处理函数实现:

     

    ///////////////////////////////////////////////////////////////////////////// 
    // CCoolListCtrl message handlers

    void CCoolListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 

        // TODO: Add your control notification handler code here 
        NMLVCUSTOMDRAW* pLVCD = reinterpret_cast <NMLVCUSTOMDRAW*> ( pNMHDR );

        *pResult = 0; 
        // Request item-specific notifications if this is the 
        // beginning of the paint cycle.

        if ( CDDS_PREPAINT  == pLVCD-> nmcd.dwDrawStage ) 
        { 
            *pResult = CDRF_NOTIFYITEMDRAW; 
        } 
        else if ( CDDS_ITEMPREPAINT == pLVCD-> nmcd.dwDrawStage ) 
        { 
            // This is the beginning of an item 's paint cycle. 
            LVITEM rItem; 
            int  nItem = static_cast <int> ( pLVCD-> nmcd.dwItemSpec ); 
            CDC* pDC = CDC::FromHandle ( pLVCD-> nmcd.hdc ); 
            COLORREF crBkgnd; 
            BOOL bListHasFocus; 
            CRect  rcItem; 
            CRect  rcText; 
            CString  sText; 
            UINT uFormat;

            bListHasFocus = ( this->GetSafeHwnd() == ::GetFocus() );

            // Get the image index and selected/focused state of the 
            // item being drawn. 
            ZeroMemory ( &rItem, sizeof(LVITEM) ); 
            rItem.mask  = LVIF_IMAGE | LVIF_STATE; 
            rItem.iItem = nItem; 
            rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 
            this->GetItem ( &rItem );

            // Get the rect that holds the item 's icon. 
            this->GetItemRect ( nItem, &rcItem, LVIR_ICON );

            // Draw the icon. 
            uFormat = ILD_TRANSPARENT;

            if ( ( rItem.state & LVIS_SELECTED ) && bListHasFocus ) 
                uFormat |= ILD_FOCUS;

            m_imagelist.Draw ( pDC, rItem.iImage, rcItem.TopLeft(), uFormat );

            // Get the rect that bounds the text label. 
            this->GetItemRect ( nItem, rcItem, LVIR_LABEL ); //把这行去掉就没有文字.

            // Draw the background of the list item. Colors are selected 
            // according to the item 's state.

            if ( rItem.state & LVIS_SELECTED ) 
            { 
                if ( bListHasFocus ) 
                { 
                    crBkgnd = GetSysColor ( COLOR_HIGHLIGHT ); 
                    pDC-> SetTextColor ( GetSysColor ( COLOR_HIGHLIGHTTEXT )); 
                } 
                else 
                { 
                    crBkgnd = GetSysColor ( COLOR_BTNFACE ); 
                    pDC-> SetTextColor ( GetSysColor ( COLOR_BTNTEXT )); 
                } 
            } 
            else 
            { 
                crBkgnd = GetSysColor ( COLOR_WINDOW ); 
                pDC-> SetTextColor ( GetSysColor ( COLOR_BTNTEXT )); 
            }

            // Draw the background & prep the DC for the text drawing. Note 
            // that the entire item RECT is filled in, so this emulates the full- 
            // row selection style of normal lists. 
            pDC-> FillSolidRect ( rcItem, crBkgnd ); 
            pDC-> SetBkMode ( TRANSPARENT );

            // Tweak the rect a bit for nicer-looking text alignment. 
            rcText = rcItem; 
            // Draw the text. 
            sText = this->GetItemText ( nItem, 0 );

            pDC-> DrawText ( sText, CRect::CRect(rcText.left+3,rcText.top,rcText.right,rcText.bottom+60), DT_VCENTER );

            // Draw a focus rect around the item if necessary. 
            if ( bListHasFocus && ( rItem.state & LVIS_FOCUSED )) 
            { 
                pDC-> DrawFocusRect ( rcItem ); 
            }

            *pResult = CDRF_SKIPDEFAULT; // We 've painted everything. 
        } 
    }

  5. 在视图类 CTestCListCtrlView 中添加成员变量 m_ListCtrl,类型为 CCoolListCtrl。
  6. 定义 WM_CREATE 消息的处理函数 CTestCListCtrlView ::Create,用于创建 CCoolListCtrl 控件,代码如下:

    //创建列表控件:自绘样式、没有列头部、处理通知 
    m_ListCtrl.Create(LVS_OWNERDRAWFIXED | LVS_NOCOLUMNHEADER | LBS_NOTIFY, CRect(0,0,400,200), this, IDC_LISTCTRL );

    // 可以使用 m_ListCtrl.SetExtendedStyle 设置扩展样式

    // 用自己编写的函数设置图像列表

    m_ListCtrl.SetImagelist(IDB_IMAGE);   

    // 用自己编写的函数设置列表项的行高,方法见文章《CListCtrl行高的修改》 
    m_ListCtrl.SetItemHeight(36); 

    // 插入项

    m_ListCtrl.InsertItem(0, "Monroeville", 0);  
    m_ListCtrl.InsertItem(1, "Hartford", 1);  
    m_ListCtrl.InsertItem(2, "Redmond", 2);

  7. 设置图像列表的函数:首先将作为图像列表的bmp文件导入到工程资源中,uBitmap是其资源ID

    BOOL CCoolListCtrl::SetImagelist(UINT uBitmap) 

        m_imagelist.DeleteImageList(); 
        m_imagelist.Create(32, 32, ILC_COLOR24|ILC_MASK, 8, 1); 
        CBitmap bitmap; 
        bitmap.LoadBitmap(uBitmap); 
        m_imagelist.Add(&bitmap, RGB(255,0,255));

        return TRUE; 
    }

        采用添加 NM_CUSTOMDRAW 消息的处理函数 OnCustomDraw 的方式来自绘,遇到了一点问题:

        创建 ClistCtrl 时使用 LVS_OWNERDRAWFIXED 样式的话,在OnCustomDraw 函数中:

    switch(pLVCD-> nmcd.dwDrawStage) //绘画阶段 
    {

            case CDDS_PREPAINT:        //在绘画前阶段 
                *pResult = CDRF_NOTIFYSUBITEMDRAW;          //返回列表项绘画通知 
                break; 
            case CDDS_ITEMPREPAINT:  //在列表项的绘画前阶段 
                *pResult = CDRF_NOTIFYSUBITEMDRAW;          //返回子列表项绘画通知 
                break; 
            case CDDS_ITEMPREPAINT|CDDS_SUBITEM:          //绘画列表项或子项 
            { 
                int iCol = lplvcd->iSubItem; 
                int iRow = lplvcd->nmcd.dwItemSpec; 
                CString sItem = GetItemText(iRow, iCol); 
                CRect rc; 
                GetCellRect(iRow, iCol, LVIR_BOUNDS, rc); 
                // get the device context. 
                CDC *pDC= CDC::FromHandle(lplvcd->nmcd.hdc); 
                // paint the text centered. 
                pDC->DrawText(sItem , rc, DT_CENTER); 
                *pResult= CDRF_SKIPDEFAULT; 
                break; 
            } 
            default: 
            *pResult = CDRF_DODEFAULT;// 控件完成自绘,不再发送 NM_CUSTOMDRAW 消息

           return ;

    }

    *pResult = CDRF_NOTIFYSUBITEMDRAW; 返回子列表项绘画通知时,弹出了错误:

    debug assertion failed

    program .....exe 
    file :winctrl2.cpp 
    line :547

    而且似乎进不了case CDDS_ITEMPREPAINT|CDDS_SUBITEM,不知何故。

    原来 CListCtrl 的派生类,使用 LVS_OWNERDRAWFIXED 的样式的话,必须重载 DrawItem 函数来绘制各项,否则就取消 LVS_OWNERDRAWFIXED 的样式。

    但取消 LVS_OWNERDRAWFIXED 的样式的话,就无法自行设置列表项的行高了。

    所以将 OnCustomDraw 换成重载 DrawItem 的方式。

    例如:

    void CCoolListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDIS)  

        LVITEM   rItem; 
        int      nItem = lpDIS->itemID; 
        CDC*     pDC = CDC::FromHandle (lpDIS->hDC); 
    //    COLORREF crBkgnd; 
        BOOL     bListHasFocus; 
        CRect    rcItem; 
        CRect    rcCell; 
        CRect    rcText; 
        CString  sText; 
        UINT     uFormat;

        LV_COLUMN lvc; 
        ::ZeroMemory(&lvc, sizeof(lvc)); 
        lvc.mask = LVCF_WIDTH | LVCF_FMT;  
        int intColumnCount;  
        for (intColumnCount=0; GetColumn(intColumnCount, &lvc); intColumnCount++) 
        { 
            bListHasFocus = ( GetSafeHwnd() == ::GetFocus() );

            // Get the image index and selected/focused state of the 
            // item being drawn. 
            ZeroMemory ( &rItem, sizeof(LVITEM) ); 
            rItem.iSubItem = intColumnCount; 
            rItem.mask  = LVIF_IMAGE | LVIF_STATE; 
            rItem.iItem = nItem; 
            rItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED; 
            VERIFY(GetItem(&rItem));

            GetSubItemRect(lpDIS->itemID, intColumnCount,LVIR_BOUNDS, rcCell);     
            if (intColumnCount == 0) 
            { 
                rcItem = rcCell; 
                rcCell.right = GetColumnWidth(0); 
            }  
            // Draw the icon. 
            uFormat = ILD_TRANSPARENT; 
             if ( ( rItem.state & LVIS_SELECTED ) && bListHasFocus ) 
                 uFormat |= ILD_FOCUS;

            // 居中绘画图标 
            CPoint picPt; 
            picPt.x = rcCell.left+(rcCell.Width()-32)/2; 
            picPt.y = rcCell.top+(rcCell.Height()-32)/2; 
            m_imagelist.Draw ( pDC, rItem.iImage, picPt, uFormat );

             // Tweak the rect a bit for nicer-looking text alignment. 
             rcText = rcItem; 
             // Draw the text. 
              sText = this->GetItemText ( nItem, 0 ); 
              pDC-> DrawText ( sText, CRect::CRect(rcText.left+3,rcText.top,rcText.right,rcText.bottom+60), DT_VCENTER );

        } 
        return ; 
    }

原创粉丝点击