自定义ListCtrl中设置背景图片的问题

本文介绍了一种自绘制列表控件的方法,通过设置LVS_OWNERDRAWFIXED标志并实现DrawItem函数来绘制每一行,同时提供了背景图片设置及滚动条响应的详细代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   自定义的列表控件必须是自绘制的,因此需要在资源编辑器中设置LVS_OWNERDRAWFIXED标志,而且还必须在自定义的控件类中实现DrawItem函数。

200781701.jpg


   代码如下:

class CListCtrlEx :  public CListCtrl
{
// Construction
public:
    CListCtrlEx();

public:

    CPalette m_pal;//调色板
    CBitmap m_bitmap;//背景位图
    int m_cxBitmap, m_cyBitmap;//背景位图高度,宽度信息
    int m_nHighlight;//高亮方式

    BOOL SetBkImage(LPCTSTR lpszResourceName);//设置背景图片
    BOOL SetBkImage(UINT nIDResource);
    int GetColumnCount();//获取列数目
    void AdjustColumnWidth();//调整列宽

// Operations
public:

// Overrides
    
// ClassWizard generated virtual function overrides
    
//{{AFX_VIRTUAL(CListCtrlEx)
    
//}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CListCtrlEx();

    // Generated message map functions
protected:
    //{{AFX_MSG(CListCtrlEx)
    afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);//水平滚动
    afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);//垂直滚动
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);//擦除背景
    afx_msg void OnPaletteChanged(CWnd* pFocusWnd);//调色板更改
    afx_msg BOOL OnQueryNewPalette();//查询新调色板
    afx_msg BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
    //}}AFX_MSG
     virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);//画每一行
    DECLARE_MESSAGE_MAP()
}
;


/////////////////////////////////////////////////////////////////////////////
//  CListCtrlEx

CListCtrlEx::CListCtrlEx()
{
   m_nHighlight=0;
}


CListCtrlEx::~CListCtrlEx()
{
}



BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
     // {{AFX_MSG_MAP(CListCtrlEx)
    ON_WM_HSCROLL()
    ON_WM_VSCROLL()
    ON_WM_ERASEBKGND()
    ON_WM_PALETTECHANGED()
    ON_WM_QUERYNEWPALETTE()     // }}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
//  CListCtrlEx message handlers
BOOL CListCtrlEx::SetBkImage(UINT nIDResource)
{
    return SetBkImage((LPCTSTR)nIDResource);
}


BOOL CListCtrlEx::SetBkImage(LPCTSTR lpszResourceName)
{
// If this is not the first call then Delete GDI objects
    if( m_bitmap.m_hObject != NULL )
        m_bitmap.DeleteObject();
    if( m_pal.m_hObject != NULL )
        m_pal.DeleteObject();


    HBITMAP hBmp = (HBITMAP)::LoadImage( AfxGetInstanceHandle(),
            lpszResourceName, IMAGE_BITMAP, 0,0, LR_CREATEDIBSECTION );

    if( hBmp == NULL )
        return FALSE;

    m_bitmap.Attach( hBmp );
    BITMAP bm;
    m_bitmap.GetBitmap( &bm );
    m_cxBitmap = bm.bmWidth;
    m_cyBitmap = bm.bmHeight;


    // Create a logical palette for the bitmap
    DIBSECTION ds;
    BITMAPINFOHEADER &bmInfo = ds.dsBmih;
    m_bitmap.GetObject( sizeof(ds), &ds );

    int nColors = bmInfo.biClrUsed ? bmInfo.biClrUsed : 1 << bmInfo.biBitCount;

    // Create a halftone palette if colors > 256. 
    CClientDC dc(NULL);            // Desktop DC
    if( nColors > 256 )
        m_pal.CreateHalftonePalette( &dc );
    else
    {
        // Create the palette

        RGBQUAD *pRGB = new RGBQUAD[nColors];
        CDC memDC;
        memDC.CreateCompatibleDC(&dc);

        memDC.SelectObject( &m_bitmap );
        ::GetDIBColorTable( memDC, 0, nColors, pRGB );

        UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * nColors);
        LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];

        pLP->palVersion = 0x300;
        pLP->palNumEntries = nColors;

        forint i=0; i < nColors; i++)
        {
            pLP->palPalEntry[i].peRed = pRGB[i].rgbRed;
            pLP->palPalEntry[i].peGreen = pRGB[i].rgbGreen;
            pLP->palPalEntry[i].peBlue = pRGB[i].rgbBlue;
            pLP->palPalEntry[i].peFlags = 0;
        }


        m_pal.CreatePalette( pLP );

        delete[] pLP;
        delete[] pRGB;
    }

    Invalidate();

    return TRUE;

}


void CListCtrlEx::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    // TODO: Add your message handler code here and/or call default
    if( m_bitmap.m_hObject != NULL ) 
        InvalidateRect(NULL);    
    
    CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}


void CListCtrlEx::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    // TODO: Add your message handler code here and/or call default
    if( m_bitmap.m_hObject != NULL )  
        InvalidateRect(NULL);    
    
    CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
}


BOOL CListCtrlEx::OnEraseBkgnd(CDC* pDC) 
{
    // TODO: Add your message handler code here and/or call default
    if( m_bitmap.m_hObject != NULL )    
        return TRUE;
    
    return CListCtrl::OnEraseBkgnd(pDC);
}


void CListCtrlEx::OnPaletteChanged(CWnd* pFocusWnd) 
{
    CListCtrl::OnPaletteChanged(pFocusWnd);
    
    // TODO: Add your message handler code here
    if( pFocusWnd == this )    
        return;

    OnQueryNewPalette();        
}


BOOL CListCtrlEx::OnQueryNewPalette() 
{
    // TODO: Add your message handler code here and/or call default
    CClientDC dc(this);
    if( dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE && m_pal.m_hObject != NULL )
    {
        dc.SelectPalette( &m_pal, FALSE );
        BOOL result = dc.RealizePalette();
        if( result ) 
            Invalidate();
        return result;
    }
    
    
    return CListCtrl::OnQueryNewPalette();
}


void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{

    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    CRect rcItem(lpDrawItemStruct->rcItem);
    int nItem = lpDrawItemStruct->itemID;
    CImageList* pImageList;

    // Save dc state
    int nSavedDC = pDC->SaveDC();

    // Get item image and state info
    LV_ITEM lvi;
    lvi.mask = LVIF_IMAGE | LVIF_STATE;
    lvi.iItem = nItem;
    lvi.iSubItem = 0;
    lvi.stateMask = 0xFFFF;        // get all state flags
    GetItem(&lvi);

    // Should the item be highlighted
    BOOL bHighlight =((lvi.state & LVIS_DROPHILITED)||((lvi.state & LVIS_SELECTED)&&((GetFocus() == this)||
        (GetStyle() & LVS_SHOWSELALWAYS))));

    // Get rectangles for drawing
    CRect rcBounds, rcLabel, rcIcon;
    GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
    GetItemRect(nItem, rcLabel, LVIR_LABEL);
    GetItemRect(nItem, rcIcon, LVIR_ICON);
    CRect rcCol( rcBounds ); 

    CString sLabel = GetItemText(nItem, 0 );

    // Labels are offset by a certain amount  
    
// This offset is related to the width of a space character
    int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;

    CRect rcHighlight;
    CRect rcClient;
    int nExt;
    switch(m_nHighlight)
    {
    case 0: 
        nExt=pDC->GetOutputTextExtent(sLabel).cx + offset;
        rcHighlight = rcLabel;
//        if( rcLabel.left + nExt 
            
        if( m_bitmap.m_hObject != NULL )
        {
               CDC tempDC;
            tempDC.CreateCompatibleDC(pDC);
            tempDC.SelectObject( &m_bitmap );

            GetClientRect(&rcClient);

            CRgn rgnBitmap;
            CRect rcTmpBmp( rcItem );
        
            rcTmpBmp.right = rcClient.right;

            // We also need to check whether it is the last item
            
// The update region has to be extended to the bottom if it is
            if( nItem == GetItemCount() - 1 )    
                rcTmpBmp.bottom = rcClient.bottom;

            rgnBitmap.CreateRectRgnIndirect(&rcTmpBmp);
            pDC->SelectClipRgn(&rgnBitmap);
            rgnBitmap.DeleteObject();
        
            if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE && m_pal.m_hObject != NULL )
            {
                pDC->SelectPalette( &m_pal, FALSE );
                pDC->RealizePalette();
            }


            CRect rcFirstItem;
            GetItemRect(0, rcFirstItem, LVIR_BOUNDS);

            for (int i = rcFirstItem.left; i < rcTmpBmp.right; i += m_cxBitmap)
                for (int j = rcFirstItem.top; j < rcTmpBmp.bottom; j += m_cyBitmap)
                    pDC->BitBlt(i, j, m_cxBitmap, m_cyBitmap, &tempDC, 0, 0, SRCCOPY);

        }

    }

    // Draw the background color
    if( bHighlight )
    {
        pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
        pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));

        pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_HIGHLIGHT)));
    }

    else if( m_bitmap.m_hObject == NULL )
        pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_WINDOW)));

    

    // Set clip region
    rcCol.right = rcCol.left + GetColumnWidth(0);
    CRgn rgn;
    rgn.CreateRectRgnIndirect(&rcCol);
    pDC->SelectClipRgn(&rgn);
    rgn.DeleteObject();

    // Draw state icon
    if (lvi.state & LVIS_STATEIMAGEMASK)
    {
        int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1;
        pImageList = GetImageList(LVSIL_STATE);
        if (pImageList)
        {
            pImageList->Draw(pDC, nImage,
                CPoint(rcCol.left, rcCol.top), ILD_TRANSPARENT);
        }

    }

    
    // Draw normal and overlay icon
    pImageList = GetImageList(LVSIL_SMALL);
    if (pImageList)
    {
        UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK;
        pImageList->Draw(pDC, lvi.iImage, 
            CPoint(rcIcon.left, rcIcon.top),
            (bHighlight?ILD_BLEND50:0) | ILD_TRANSPARENT | nOvlImageMask );
    }


    
    
    // Draw item label - Column 0
    rcLabel.left += offset/2;
    rcLabel.right -= offset;

    pDC->DrawText(sLabel,-1,rcLabel,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP 
                | DT_VCENTER | DT_END_ELLIPSIS);


    // Draw labels for remaining columns
    LV_COLUMN lvc;
    lvc.mask = LVCF_FMT | LVCF_WIDTH;

    if( m_nHighlight == 0 )        // Highlight only first column
    {
        pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
        pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
    }

    
    rcBounds.right = rcHighlight.right > rcBounds.right ? rcHighlight.right :
                            rcBounds.right;
    rgn.CreateRectRgnIndirect(&rcBounds);
    pDC->SelectClipRgn(&rgn);
                   
    for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
    {
        rcCol.left = rcCol.right;
        rcCol.right += lvc.cx;

        // Draw the background if needed&& m_nHighlight == HIGHLIGHT_NORMAL
        if( m_bitmap.m_hObject == NULL  )
            pDC->FillRect(rcCol, &CBrush(::GetSysColor(COLOR_WINDOW)));

        sLabel = GetItemText(nItem, nColumn);
        if (sLabel.GetLength() == 0)
            continue;


        // Get the text justification
        UINT nJustify = DT_LEFT;
        switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
        {
        case LVCFMT_RIGHT:
            nJustify = DT_RIGHT;
            break;
        case LVCFMT_CENTER:
            nJustify = DT_CENTER;
            break;
        default:
            break;
        }


        rcLabel = rcCol;
        rcLabel.left += offset;
        rcLabel.right -= offset;

        pDC->DrawText(sLabel, -1, rcLabel, nJustify | DT_SINGLELINE 
                | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS);
    }


    // Draw focus rectangle if item has focus
    if (lvi.state & LVIS_FOCUSED && (GetFocus() == this))
        pDC->DrawFocusRect(rcHighlight);

    
    // Restore dc
    pDC->RestoreDC( nSavedDC );

}


void CListCtrlEx::AdjustColumnWidth()
{
    SetRedraw(FALSE);
    int nColumnCount = GetColumnCount();

    for(int i = 0; i < nColumnCount; i++)
    {
        SetColumnWidth(i, LVSCW_AUTOSIZE);
        int nColumnWidth = GetColumnWidth(i);
        SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
        int nHeaderWidth = GetColumnWidth(i);

        SetColumnWidth(i, max(nColumnWidth, nHeaderWidth));
    }

    SetRedraw(TRUE);
}


int CListCtrlEx::GetColumnCount()
{
    CHeaderCtrl* pHeaderCtrl = GetHeaderCtrl();
    return(pHeaderCtrl->GetItemCount());
}


BOOL CListCtrlEx::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    HD_NOTIFY    *pHDN = (HD_NOTIFY*)lParam;

    // This code is for using bitmap in the background
    
// Invalidate the right side of the control when a column is resized
    if(pHDN->hdr.code == HDN_ITEMCHANGINGW || pHDN->hdr.code == HDN_ITEMCHANGINGA)
    {
        if( m_bitmap.m_hObject != NULL )
        {
            CRect rcClient;
            GetClientRect( &rcClient );
            DWORD dwPos = GetMessagePos();
            CPoint pt( LOWORD(dwPos), HIWORD(dwPos) );
            ScreenToClient( &pt );
            rcClient.left = pt.x;
            InvalidateRect( &rcClient );
        }

    }

    return CListCtrl::OnNotify(wParam, lParam, pResult);
}


BOOL CCdDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Add "About" menu item to system menu.

    
// IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        CString strAboutMenu;
        strAboutMenu.LoadString(IDS_ABOUTBOX);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }

    }


    // Set the icon for this dialog.  The framework does this automatically
    
//  when the application's main window is not a dialog
    SetIcon(m_hIcon, TRUE);            // Set big icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    m_list.InsertColumn(0,_T("用户编号"));
    m_list.InsertColumn(1,_T("  用户名称  "));
    m_list.InsertColumn(2, _T("年龄"));
    m_list.InsertColumn(3, _T("性别"));
    m_list.InsertColumn(4,_T(" 用户住址 "));
    char ch[5];

    for(int j=1;j<=200;j++)
    {
        m_list.InsertItem(j,itoa(j,ch,10));
    }


    for(int i = 0;i<200;i++)
    {
        
        m_list.SetItemText(i,1,strcat(itoa(i+1,ch,10),"号用户"));
        m_list.SetItemText(i,2,itoa((i+100)%100,ch,10));
        if(i%2==0)
        {
            m_list.SetItemText(i,3,"男");
        }

        else
        {
            m_list.SetItemText(i,3,"女");
        }

        m_list.SetItemText(i,4,strcat(itoa(i+1,ch,10),"号大街"));
    }


    
      
      ListView_SetExtendedListViewStyle(m_list.m_hWnd,LVS_EX_FULLROWSELECT|LVS_EX_FLATSB|LVS_EX_HEADERDRAGDROP );    
    m_list.SetBkImage(IDB_Bless);
    m_list.AdjustColumnWidth();
    return TRUE;  // return TRUE  unless you set the focus to a control
}

效果如图所示:
200781702.jpg
   但是这里还有一个问题,就是当下拉滚动条的时候,如何让背景图片不动,而只是数据行变化,这是接下来要做的工作。

 

参考资料:

http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c983/

作者:洞庭散人

出处:http://phinecos.cnblogs.com/    

本博客遵从 Creative Commons Attribution 3.0 License,若用于非商业目的,您可以自由转载,但请保留原作者信息和文章链接URL。

0
0
(请您对文章做出评价)
« 上一篇: OpenGL中的鼠标控制
» 下一篇: 用Soap消息调用Web Services

posted on 2007-08-17 11:10 Phinecos(洞庭散人) 阅读(3854) 评论(11编辑 收藏

评论

#1楼 2007-10-26 11:54 wangyi [未注册用户]
你好,我想问一下如何让他在滚动的时候背景不动呢?我现在主要是要解决自绘的ListCtrl中的闪屏问题,经过我这几天的发现,觉得如果绘的时候背景不动的话,会好很多,有什么办法可以让他背景不动吗?
  
#2楼[楼主2007-10-26 12:03 Phinecos(洞庭散人)  
和我想做的一样,我也搞不好这个问题。。。
  
#3楼 2007-10-29 10:05 wangyi [未注册用户]
谢谢楼主啊!你如果发现什么好的方法,可以告诉我吗?
  
#4楼 2008-04-09 13:01 花满楼[未注册用户]
谢谢楼主贴出这么详细的代码,给我们新手不少帮助。不知道楼主“但是这里还有一个问题,就是当下拉滚动条的时候,如何让背景图片不动,而只是数据行变化,这是接下来要做的工作。”这部分的工作做的怎么样了,做出来了的话能不能贴出来,或者发给我呢? 
谢谢! 
  
#5楼 2008-10-20 11:14 guyuewuhua[未注册用户]
还有一个问题就是,当没有数据显示时,背景图不显示,
  
#6楼 2008-12-23 11:16 80676535[未注册用户]
保持背景不动,只改数据.只要在每一次有动作的时候重新画图就好了 
加上 
pDC->SelectObject( &m_bitmap ); 
把下面的注掉,就OK了,细节自己加工吧 
CDC tempDC; 
tempDC.CreateCompatibleDC(pDC); 
tempDC.SelectObject( &m_bitmap ); 

GetClientRect(&rcClient); 

CRgn rgnBitmap; 
CRect rcTmpBmp( rcItem ); 

rcTmpBmp.right = rcClient.right; 

// We also need to check whether it is the last item 
// The update region has to be extended to the bottom if it is 
if( nItem == GetItemCount() - 1 ) 
rcTmpBmp.bottom = rcClient.bottom; 

rgnBitmap.CreateRectRgnIndirect(&rcTmpBmp); 
pDC->SelectClipRgn(&rgnBitmap); 
rgnBitmap.DeleteObject(); 

if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE && m_pal.m_hObject != NULL ) 

pDC->SelectPalette( &m_pal, FALSE ); 
pDC->RealizePalette(); 


CRect rcFirstItem; 
GetItemRect(0, rcFirstItem, LVIR_BOUNDS); 

for (int i = rcFirstItem.left; i < rcTmpBmp.right; i += m_cxBitmap) 
for (int j = rcFirstItem.top; j < rcTmpBmp.bottom; j += m_cyBitmap) 
pDC->BitBlt(i, j, m_cxBitmap, m_cyBitmap, &tempDC, 0, 0, SRCCOPY); 
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值