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

代码如下:
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;

for( int 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
}
效果如图所示:

但是这里还有一个问题,就是当下拉滚动条的时候,如何让背景图片不动,而只是数据行变化,这是接下来要做的工作。
参考资料:
http://www.codeguru.com/Cpp/controls/listview/backgroundcolorandimage/article.php/c983/