让CMyListCtrl 显示彩色图片作为头像很容易,用CImageList 加载规格相同的图片到其中,然后让CimageList和CMyListCtrl关联就可能实现。往ImageList 添加图片或图标有三种方法,代码如下
HICON hIcon = (HICON)LoadImage(NULL, ".//image//SQQun.ico", IMAGE_ICON, 0, 0,
LR_LOADFROMFILE)
pbitmap ->m_hObject = (HBITMAP) LoadImage(NULL, "face.bmp", IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE);
….
CRect(50,100,206,180),
this,IDD_TALKER_LIST/*ID*/))
m_MyTalkerListCtrl.SetImageList(&m_imagelistBig,LVSIL_SMALL);
m_MyTalkerListCtrl.InsertItem(i,"我的好友", i);//第三个参数表示Image 的Index
显灰色图片似乎不好办,是做大量的单色位图文件还是用软件代码实现转换?做文件麻烦,或几经考虑,最好是把ImageList 中彩色图片用代码实现单色位图转换。处理方法是
HBITMAP oldBitmap = (HBITMAP)memDC.SelectObject(hBitmap);
for(int W = 0; W <= bmpInfo.bmWidth; W ++)
r = GetRValue(memDC.GetPixel(W,H));
g = GetGValue(memDC.GetPixel(W,H));
b = GetBValue(memDC.GetPixel(W,H));
r = (r * 3 + g * 6 + g) / 10;
memDC.SetPixel(CPoint(W,H),RGB(r,g,b));
hBitmap = (HBITMAP)memDC.SelectObject(oldBitmap);
QQ 的ListCtrl 包含了很多信息,如在线用户和不在用户的头象不同,有视频设备用户会显示标志,开通了手机短消息功能的也会显示标志,还有很多不一一例出。这是如何实现的?去MSDN分析CListCtrl 发现,有两个函数SetItemData(int nItem,DWORD dwData),和GetItemData(int nItem),非常有用,这个32位 data 做几个标志还是不错的,但还是无法表达更多的东东。如果把这32位 data作为外部结构的地址是否可行呢?经实验是可行的,但在要外部处理,封装性能不好! 那自己定一个为用户信息结构吧!
4. 当新消息时动画显示头像和新消息数量(在V1.2版本中实现)
二、打开Visual Studio C++ (6.0),新建工程。(本文的主要任务是完成一个QQ ListCtrl 的,不在本任务下的其它工作请自己完成,若和ListCtrl使用有关的代码,本文会详细介绍)
a.
首先,生成一个新类名为CMyListCtrl. 其基类为CListCtrl. 这部分工作用ClassWizard很容易完成。
b.
添加相关消息及处理函数,OnPaint() ;OnMouseMove();OnHScroll();OnVScroll等,这部工作用ClassWizard同样很容易完成。编译通过后,接着往下看。
c. 在.h文件顶部定义结构体
struct LUSERITEM ,可参照下面定义
d. 在.h文件顶部定义一些常量标志
#define TVS_VIDEO 0x00000001
#define TVS_MOBILEMSG 0x00000002
#define TVS_NETDISK 0x00000004
#define TVS_LEADER 0x00000008
#define TVS_VICELEADER 0x00000010
#define TVS_ONLINEUSER 0x00000020
e.添加成员变量 及函数
CFont* m_pFont; //用于创建选择字体
BOOL m_bOverImage;
BOOL m_bOverVedio;
BOOL m_bOverMobile;
DEQUELVITEM m_DequeList; //用户信息链表
HICON m_hTailIconA; //vido flag
HICON m_hTailIconB; //mobil message flag
HICON m_hTailIconC;
HBITMAP m_hBackBitmap; //背景
f. .添加成员函数
重载InsertItem函数,用于增加Item同时增加用户信息。
InsertItem(int nItem, LPCTSTR szItemText, int nImageIndex, LUSERITEM* UserInfo)
{
DEQUELVITEM* pDeqListItem = &m_DequeList;
if(UserInfo)
pDeqListItem ->push_back(*UserInfo);
nItem = CListCtrl::InsertItem(nItem,szItemText,nImageIndex);
return nItem;
}
添加设置显示图标函数,A指定视频标志图标,B指定为短消息标志图标,C未定义
void CMyListCtrl::SetTailIcon(LPCTSTR strIconFileA,LPCTSTR strIconFileB,
LPCTSTR strIconFileC)
{
HICON hIcon=NULL;
hIcon = (HICON)::LoadImage(NULL, strIconFileA, IMAGE_ICON, 0, 0,
LR_DEFAULTSIZE|LR_LOADFROMFILE);
if(hIcon)
{
if(m_hTailIconA)
DeleteObject(m_hTailIconA);
m_hTailIconA = hIcon;
}
hIcon = (HICON)::LoadImage(NULL, strIconFileB, IMAGE_ICON, 0, 0,
LR_DEFAULTSIZE|LR_LOADFROMFILE);
if(hIcon)
{
if(m_hTailIconB)
DeleteObject(m_hTailIconB);
m_hTailIconB = hIcon;
}
hIcon = (HICON)::LoadImage(NULL, strIconFileC, IMAGE_ICON, 0, 0,
LR_DEFAULTSIZE|LR_LOADFROMFILE);
if(hIcon)
{
if(m_hTailIconC)
DeleteObject(m_hTailIconC);
m_hTailIconC = hIcon;
}
}
添加设背景位图函数SetBackBitmap
void CMyListCtrl::SetBackBitmap(LPCTSTR lpszResourceName)
{
HBITMAP hBmp = (HBITMAP)::LoadImageFile(lpszResourceName);
if(hBmp)
m_hBackBitmap = hBmp;
}
添加删除用户信息函数
BOOL DeleteUserInfo(CString szText)
{
BOOL bRet = FALSE;
LUSERITEM itemInfo;
DEQUELVITEM* pDeqItem = &m_DequeList;
int nItemCount = -1;
DEQUELVITEM::iterator it,itbegin = pDeqItem->begin(),itend = pDeqItem->end();
for ( it = itbegin; it != itend; it++ )
{
nItemCount++;
if(( it->szUserID == szText)||(it->szUserName == szText))
{
if ( nItemCount == ( pDeqItem->size() - 1 ) )
{
//如果是最后一个
pDeqItem->pop_back();
}
else if ( nItemCount == 0 )
//如果是第一个
pDeqItem->pop_front();
else
pDeqItem->erase( pDeqItem->begin() + nItemCount );
bRet = TRUE;
}
}
return bRet;
}
}
实现自绘代码在OnPaint()中添加 下面给出全部代CMyListCtrl代码:
//.h文件代码
#if !defined(AFX_MYLISTCTRL_H__4813720C_157E_48C4_9F2F_9A15E2B93038__INCLUDED_)
#define AFX_MYLISTCTRL_H__4813720C_157E_48C4_9F2F_9A15E2B93038__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "GeneralApi.h"
#include
// MyListCtrl.h : header file
//
#define TVS_VIDEO 0x00000001
#define TVS_MOBILEMSG 0x00000002
#define TVS_NETDISK 0x00000004
#define TVS_LEADER 0x00000008
#define TVS_VICELEADER 0x00000010
#define TVS_ONLINEUSER 0x00000020
/////////////////////////////////////////////////////////////////////////////
// CMyListCtrl window
struct LUSERITEM
{
CString szUserID;
CString szUserName;
CString szIPAddress;
CString szServerAddress;
CString szNoticeMsg;
BOOL bOnline;
int nHeadImageIndex;
};
typedef std::deque DEQUELVITEM;
class CMyListCtrl : public CListCtrl
{
// Construction
public:
CMyListCtrl();
// Attributes
public:
//HBITMAP BitmapColorToGray(CDC* pDC,HBITMAP hBitmap);
int InsertItem(int nItem, LPCTSTR szItemText, int nImageIndex, LUSERITEM* UserInfo = NULL);
BOOL GetUserItemInfo(LPCTSTR szItemText,LUSERITEM* userInfo);
void SetTailIcon(LPCTSTR strIconFileA,LPCTSTR strIconFileB,LPCTSTR strIconFileC);
void SetBackBitmap(LPCTSTR lpszResourceName);
BOOL GetClickMobileBtn();
BOOL GetClickVideoBtn();
BOOL DeleteUserInfo(CString szText);
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMyListCtrl)
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMyListCtrl();
virtual void DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct);
// Generated message map functions
protected:
//{{AFX_MSG(CMyListCtrl)
afx_msg void OnPaint();
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
CFont* m_pFont;
BOOL m_bOverImage;
BOOL m_bOverVedio;
BOOL m_bOverMobile;
DEQUELVITEM m_DequeList;
HICON m_hTailIconA;
HICON m_hTailIconB;
HICON m_hTailIconC;
HBITMAP m_hBackBitmap;
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MYLISTCTRL_H__4813720C_157E_48C4_9F2F_9A15E2B93038__INCLUDED_)
.CPP 文件代码
// MyListCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "MyListCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMyListCtrl
CMyListCtrl::CMyListCtrl()
{
m_pFont=new CFont();
m_pFont->CreatePointFont(90,_T("Arial"));
m_bOverImage = FALSE;
m_bOverVedio = FALSE;
m_bOverMobile = FALSE;
m_hTailIconA = NULL;
m_hTailIconB = NULL;
m_hTailIconC = NULL;
m_hBackBitmap = NULL;
}
CMyListCtrl::~CMyListCtrl()
{
delete m_pFont;
if(m_hTailIconA)
::DestroyIcon(m_hTailIconA);
if(m_hTailIconB)
::DestroyIcon(m_hTailIconB);
if(m_hTailIconC)
::DestroyIcon(m_hTailIconC);
if(m_hBackBitmap)
DeleteObject(m_hBackBitmap);
}
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CMyListCtrl)
ON_WM_PAINT()
ON_WM_DRAWITEM()
ON_WM_MOUSEMOVE()
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_ERASEBKGND()
ON_WM_VSCROLL()
ON_WM_HSCROLL()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMyListCtrl message handlers
void CMyListCtrl::OnPaint()
{
CPaintDC dc(this);
CRect rcClip, rcClient;
dc.GetClipBox( &rcClip );
GetClientRect(&rcClient);
// Create a compatible memory DC
CDC memDC;
if(!memDC.CreateCompatibleDC( &dc ))
return;
// Select a compatible bitmap into the memory DC
CBitmap bitmap, bmpImage;
if(!bitmap.CreateCompatibleBitmap( &dc, rcClient.Width(), rcClient.Height()))
{
memDC.DeleteDC();
return;
}
CBitmap *OldBitmap = memDC.SelectObject(&bitmap );
memDC.BitBlt(rcClient.left,rcClient.top,rcClient.Width(), rcClient.Height(),&dc,rcClient.left,rcClient.top,SRCCOPY);
// First let the control do its default drawing.
//CListCtrl::DefWindowProc( WM_PAINT, (WPARAM)memDC.m_hDC, 0 );
if( m_hBackBitmap )
{
DrawBitmap(&memDC,m_hBackBitmap,rcClient);
}
//DrawItem here
int nItemCount = GetItemCount();
int nItem = 0;
CRect rcItem,rcText,rcIcon;
POINT pt;
GetCursorPos(&pt);
ScreenToClient(&pt);
while ((nItem < nItemCount) && (nItem >=0))
{
GetItemRect(nItem,rcItem, LVIR_BOUNDS); //get Rectangle of the icon and Lable
GetItemRect(nItem,rcText, LVIR_LABEL);
GetItemRect(nItem,rcIcon, LVIR_ICON);
//对非在线用户的图像进单色处理
DWORD nStyle = GetItemData(nItem);
//if (!(nStyle & TVS_ONLINEUSER))
//{
CImageList* pImageList=NULL;
pImageList = GetImageList(LVSIL_SMALL);
if(pImageList !=NULL)
{
HICON hIcon=NULL;
hIcon = pImageList->ExtractIcon(nItem);
HBITMAP hbitmap,hBitmapMask;
ICONINFO* iconinfo = new ICONINFO;
if(::GetIconInfo(hIcon, iconinfo))
{
hbitmap = iconinfo->hbmColor;
hBitmapMask = iconinfo->hbmMask;
if (!(nStyle & TVS_ONLINEUSER))
hbitmap = BitmapColorToGray(&memDC,hbitmap);
DrawBitmap(&memDC,hbitmap,rcIcon);
DeleteObject(hbitmap);
DeleteObject(hBitmapMask);
}
delete iconinfo;
::DestroyIcon(hIcon);
}
//}
//对图像进行加边框处理
CBrush *pOldBrush = (CBrush*) memDC.SelectStockObject(NULL_BRUSH);
CPen imagePen;
if(rcIcon.PtInRect(pt))
{
imagePen.CreatePen(PS_SOLID, 2, RGB(192, 192, 200));
}
else
{
imagePen.CreatePen(PS_SOLID, 1, RGB(0, 0, 200));
}
CPen* pOldPen = memDC.SelectObject(&imagePen);
CRect rcImage(rcIcon);
rcImage.DeflateRect(1,1);
memDC.RoundRect(rcIcon,CPoint(10,10));
memDC.SelectObject(pOldPen);
memDC.SelectObject(pOldBrush);
imagePen.DeleteObject();
//文本输出
CRect rcNewText(rcText);
CString strText = GetItemText(nItem,0);
CSize size = memDC.GetTextExtent(strText);
rcNewText.top = rcNewText.top-2;
rcNewText.right = rcNewText.right +3;
if(!m_hBackBitmap)
memDC.BitBlt(rcNewText.left,rcNewText.top,rcNewText.Width()+2,rcNewText.Height()+2,&dc,rcNewText.left,rcNewText.top,SRCCOPY);
if(rcItem.PtInRect(pt))
{
memDC.SetTextColor(RGB(0,0,255));
}
else
{
memDC.SetTextColor(RGB(0,0,0));
}
memDC.SetBkMode(TRANSPARENT);
memDC.SelectObject(m_pFont);
rcNewText.OffsetRect(3,0);
memDC.DrawText(strText,rcNewText,DT_SINGLELINE|DT_LEFT|DT_END_ELLIPSIS);
LUSERITEM userInfo;
GetUserItemInfo(strText,&userInfo);
if(userInfo.szUserID !="")
{
strText = userInfo.szNoticeMsg;
rcNewText.top = rcNewText.top + size.cy + 2;
rcNewText.right = rcClient.right -10;
if(rcNewText.PtInRect(pt))
{
memDC.SetTextColor(RGB(10,255,10));
}
else
{
memDC.SetTextColor(GetSysColor(COLOR_BTNSHADOW));
}
memDC.DrawText(strText,rcNewText,DT_SINGLELINE|DT_LEFT|DT_END_ELLIPSIS);
}
//Draw Vedio and Mobile ICON
CRect rcVideo(rcText);
rcVideo.left = rcClient.right - 20;
rcVideo.right = rcVideo.left +14;
rcVideo.top = rcVideo.top + 2;
rcVideo.bottom = rcVideo.top + 14;
if ( (nStyle & TVS_VIDEO) && (m_hTailIconA !=NULL) )
{
if(!m_hBackBitmap)
memDC.FillSolidRect(rcVideo,RGB(255,255,255));
::DrawIconEx(memDC,rcVideo.left,rcVideo.top,m_hTailIconA,
12,12,0,NULL,DI_NORMAL);
if( rcVideo.PtInRect(pt) )
{
memDC.Draw3dRect(rcVideo,GetSysColor(COLOR_BTNSHADOW),GetSysColor(COLOR_BTNSHADOW));
m_bOverVedio =TRUE;
}
else
{
m_bOverVedio =FALSE;
}
}
CRect rcMobile(rcVideo);
rcMobile.OffsetRect(-16,0);
if ( (nStyle & TVS_MOBILEMSG) && (m_hTailIconB !=NULL) )
{
if(!m_hBackBitmap)
memDC.FillSolidRect(rcMobile,RGB(255,255,255));
::DrawIconEx(memDC,rcMobile.left+1,rcMobile.top+1,m_hTailIconB,
12,12,0,NULL,DI_NORMAL);
if( rcMobile.PtInRect(pt) )
{
memDC.Draw3dRect(rcMobile,GetSysColor(COLOR_BTNSHADOW),GetSysColor(COLOR_BTNSHADOW));
m_bOverMobile =TRUE;
}
else
{
m_bOverMobile =FALSE;
}
}
///////////////
nItem = GetNextItem(nItem,LVNI_ALL);
}
//DrawItem end
dc.BitBlt( rcClip.left, rcClip.top,
rcClient.Width(),
rcClient.Height(),
&memDC,
rcClip.left, rcClip.top, SRCCOPY );
//memDC.SelectObject(OldBitmap);
bitmap.DeleteObject();
memDC.DeleteDC();
}
void CMyListCtrl::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your message handler code here and/or call default
CListCtrl::OnDrawItem(nIDCtl, lpDrawItemStruct);
}
void CMyListCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
Invalidate(FALSE);
CListCtrl::OnMouseMove(nFlags, point);
}
void CMyListCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
ModifyStyle(0, BS_OWNERDRAW);
CListCtrl::PreSubclassWindow();
}
void CMyListCtrl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct)
{
}
int CMyListCtrl::InsertItem(int nItem, LPCTSTR szItemText, int nImageIndex, LUSERITEM* UserInfo)
{
DEQUELVITEM* pDeqListItem = &m_DequeList;
if(UserInfo)
pDeqListItem ->push_back(*UserInfo);
nItem = CListCtrl::InsertItem(nItem,szItemText,nImageIndex);
return nItem;
}
void CMyListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
NMTVCUSTOMDRAW* pListHDR = (NMTVCUSTOMDRAW*)pNMHDR;
DWORD dwDrawStage;
*pResult = CDRF_DODEFAULT;
dwDrawStage = pListHDR->nmcd.dwDrawStage;
if(dwDrawStage == CDDS_PREPAINT)
{
*pResult = CDRF_NOTIFYITEMDRAW;
}
else if(dwDrawStage == CDDS_ITEMPREPAINT)
{
}
}
BOOL CMyListCtrl::GetUserItemInfo(LPCTSTR szItemText,LUSERITEM* userInfo)
{
ASSERT(userInfo !=NULL);
BOOL bRet = FALSE;
DEQUELVITEM* pDeqListItem = &m_DequeList;
DEQUELVITEM::iterator it,itbegin = pDeqListItem->begin(),itend = pDeqListItem->end();
for ( it = itbegin; it != itend; it++ )
{
if( it->szUserID == szItemText)
{
*userInfo = (LUSERITEM)*it; //复制结构数据到userInof;
bRet = TRUE;
break;
}
}
return bRet;
}
void CMyListCtrl::SetTailIcon(LPCTSTR strIconFileA,LPCTSTR strIconFileB,LPCTSTR strIconFileC)
{
HICON hIcon=NULL;
hIcon = (HICON)::LoadImage(NULL, strIconFileA, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
if(hIcon)
{
if(m_hTailIconA)
DeleteObject(m_hTailIconA);
m_hTailIconA = hIcon;
}
hIcon = (HICON)::LoadImage(NULL, strIconFileB, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
if(hIcon)
{
if(m_hTailIconB)
DeleteObject(m_hTailIconB);
m_hTailIconB = hIcon;
}
hIcon = (HICON)::LoadImage(NULL, strIconFileC, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
if(hIcon)
{
if(m_hTailIconC)
DeleteObject(m_hTailIconC);
m_hTailIconC = hIcon;
}
}
BOOL CMyListCtrl::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return CListCtrl::OnEraseBkgnd(pDC);
}
void CMyListCtrl::SetBackBitmap(LPCTSTR lpszResourceName)
{
HBITMAP hBmp = (HBITMAP)::LoadImageFile(lpszResourceName);
if(hBmp)
m_hBackBitmap = hBmp;
}
void CMyListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
Invalidate(FALSE);
CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CMyListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
Invalidate(FALSE);
CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}
BOOL CMyListCtrl::GetClickMobileBtn()
{
return m_bOverMobile;
}
BOOL CMyListCtrl::GetClickVideoBtn()
{
return m_bOverVedio;
}
BOOL CMyListCtrl::DeleteUserInfo(CString szText)
{
BOOL bRet = FALSE;
LUSERITEM itemInfo;
DEQUELVITEM* pDeqItem = &m_DequeList;
int nItemCount = -1;
DEQUELVITEM::iterator it,itbegin = pDeqItem->begin(),itend = pDeqItem->end();
for ( it = itbegin; it != itend; it++ )
{
nItemCount++;
if(( it->szUserID == szText)||(it->szUserName == szText))
{
if ( nItemCount == ( pDeqItem->size() - 1 ) )
{
//如果是最后一个
pDeqItem->pop_back();
}
else if ( nItemCount == 0 )
//如果是第一个
pDeqItem->pop_front();
else
pDeqItem->erase( pDeqItem->begin() + nItemCount );
bRet = TRUE;
}
}
return bRet;
}