QQ软件主界面的ListCtrl 是可以说非常经典了,一个字漂亮! 这个ListCtrl的所包含的信息之丰富,更是让我们这些软件工程师望Q兴叹! 今天,我将和大家一起来写一个属于自已的CMyListCtrl。
一、实现CMyListCtrl要完成的任务及实现方法分析。
1.MyListCtrl 显示彩色图片头像(在线用户头)
让CMyListCtrl 显示彩色图片作为头像很容易,用CImageList 加载规格相同的图片到其中,然后让CimageList和CMyListCtrl关联就可实现, 向ImageList 添加图片或图标的三种方法代码总结如下:
01.
CImageList m_imageList;
02.
03.
m_imagelist.Create(40, 40, ILC_MASK|ILC_COLOR32, 1, 1);
04.
05.
//添加ID 为IDI_ICON的图标
06.
07.
m_imageList.Add( AfxGetApp()->LoadIcon(IDI_ICON));
08.
09.
//从图标文件中加载并添加
10.
11.
HICON
hIcon = (
HICON
)LoadImage(NULL,
".\\image\\SQQun.ico"
, IMAGE_ICON, 0, 0, LR_LOADFROMFILE)
12.
13.
m_imagelist.Add(hIcon);
14.
15.
//从位图文件中加载并添加
16.
17.
CBitmap *pBitmap=
new
CBitmap;
18.
19.
pbitmap ->m_hObject = (
HBITMAP
) LoadImage(NULL,
"face.bmp"
, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
20.
m_imagelist.Add(pBitmap, RGB(255,255, 255)
/*mask color*/
);
21.
22.
…
23.
24.
Delete pBitmap;
把 CMyListCtrl 和CimageList 关联并向ListCtrl 添加用户代码
01.
//先创建ListCtrl (m_MyFriendListCtrl)
02.
03.
if
(m_MyFriendListCtrl.Create(LVS_SMALLICON | WS_TABSTOP|WS_CHILD,
04.
CRect(50,100,206,180),
this
, IDD_TALKER_LIST
/*ID*/
))
05.
{
06.
//关联
07.
08.
m_MyFriendListCtrl.SetImageList(&m_imagelistBig,LVSIL_SMALL);
09.
m_MyFriendListCtrl.SetBackBitmap(_T(
".\\image\\mainFrame-centerMid.bmp"
));
10.
11.
//往ListCtrl 中添加好友
12.
13.
for
(
int
i=0; i<10; i++)
14.
{
15.
CString strTemp;
16.
strTemp.Format(
"我的好友%d"
,i);
17.
18.
LUSERITEM userInfo;
19.
20.
userInfo.szUserID = strTemp.GetBuffer(strTemp.GetLength());
21.
strTemp.ReleaseBuffer();
22.
userInfo.szNoticeMsg = _T(
"天不怕!地不怕!"
);
23.
m_MyFriendListCtrl.InsertItem(i,strTemp, i,&userInfo);
//第三个参数 Index of the ImageList
24.
m_MyFriendListCtrl.SetItemData(i,(i%2)? i|0x00000020:i);
25.
}
26.
27.
m_MyFriendListCtrl.ShowWindow(SW_SHOW);
28.
}
2.让CMyListCtrl 显示灰色图片头像(非在线用户)
显示灰色图片的方法可用图像处理软件处理成单色位图文件后使用,也可用软件代码实现转换,前者的方法处理位图文件个数不多是还行,否则就显得麻烦了,其优点运行速度快。用软件代码转换的方法也是可行的,象LoadImage ()或CopyImage()API都可实现,但要占用很多CUP时间。对比两种方法,我选择后者,原因不用说我想大家也清楚。
实现彩色图片到单色位图转换的方法是先获取CMyListCtrl的ImageList 并提取ListCtrl中的Item 对应的图像后,用代码转换成单色位图并在原位置显示。转换过程如下:
01.
CImageList* pImageList=NULL;
02.
pImageList = GetImageList(LVSIL_SMALL);
03.
if
(pImageList !=NULL)
04.
{
05.
HICON
hIcon=NULL;
06.
hIcon = pImageList->ExtractIcon(nItem);
07.
HBITMAP
hbitmap,hBitmapMask;
08.
ICONINFO* iconinfo =
new
ICONINFO;
09.
if
(::GetIconInfo(hIcon, iconinfo))
10.
{
11.
hbitmap = iconinfo->hbmColor;
12.
hBitmapMask = iconinfo->hbmMask;
13.
if
(!(nStyle & TVS_ONLINEUSER))
14.
//hbitmap = BitmapColorToGray(m_hDll,&memDC,hbitmap);
15.
hbitmap = (
HBITMAP
) CopyImage(hbitmap, IMAGE_BITMAP,0, 0,LR_COPYDELETEORG|LR_MONOCHROME);
16.
DrawBitmap(m_hDll, &memDC,hbitmap,rcIcon);
17.
DeleteObject(hbitmap);
18.
DeleteObject(hBitmapMask);
19.
}
20.
delete
iconinfo;
21.
::DestroyIcon(hIcon);
22.
}
实现彩色图片到单色位图转换的语句为:
1.
hbitmap = (
HBITMAP
) CopyImage(hbitmap, IMAGE_BITMAP,0, 0,LR_COPYDELETEORG|LR_MONOCHROME);
我也写了一个实现彩色图片到单色位图转换算法,其代码如下:
01.
//这是本人写的一个转换算法。效果好,但运行时间稍长
02.
HBITMAP
BitmapColorToGray(CDC* pDC,
HBITMAP
hBitmap)
03.
{
04.
BITMAP bmpInfo;
05.
06.
::GetObject(hBitmap,
sizeof
(BITMAP),&bmpInfo);
07.
08.
if
(pDC)
09.
{
10.
CDC memDC;
11.
12.
if
( !memDC.CreateCompatibleDC(pDC) )
13.
{
14.
return
NULL;
15.
}
16.
17.
HBITMAP
oldBitmap = (
HBITMAP
)memDC.SelectObject(hBitmap);
18.
19.
DWORD
r,g,b;
20.
21.
for
(
int
H =0; H <= bmpInfo.bmHeight; H++)
22.
{
23.
for
(
int
W = 0; W <= bmpInfo.bmWidth; W ++)
24.
{
25.
r = GetRValue(memDC.GetPixel(W,H));
26.
g = GetGValue(memDC.GetPixel(W,H));
27.
b = GetBValue(memDC.GetPixel(W,H));
28.
r = (r * 3 + g * 6 + g) / 10;
29.
g = r;
30.
b = g;
31.
memDC.SetPixel(CPoint(W,H),RGB(r,g,b));
32.
}
33.
}
34.
35.
hBitmap = (
HBITMAP
)memDC.SelectObject(oldBitmap);
36.
37.
memDC.DeleteDC();
38.
}
39.
40.
return
hBitmap;
41.
}
3.MyListCtrl 要包含丰富的用户信息(如 ID,NAME 、IP Address 、视频可用 ,手机短消息等用户信息)
QQ 的ListCtrl 包含了很多信息,如在线用户和不在线用户的头象不同,有视频设备的用户还会显示标志,开通了手机短消息功能的也会显示标志,等等。这是如何实现的?找MSDN分析CListCtrl 发现,有两个函数SetItemData(int nItem,DWORD dwData),和DWORD GetItemData(int nItem),非常有用,这个32位 data 做几个标志还是不错的,但还是无法表达更多的东东。如果把这32位 data作为外部结构的地址是否可行呢?经实验是可行的,但在要外部处理,封装性能不好! 于是定义了一个用户信息的结构。
01.
struct
LUSERITEM
02.
{
03.
CString szUserID;
04.
CString szUserName;
05.
CString szIPAddress;
06.
CString szServerAddress;
07.
CString szNoticeMsg;
08.
BOOL
bOnline;
09.
int
nHeadImageIndex;
10.
11.
//根据需要可增加信息
12.
};
再定义一个链表,用来管理用户信息的结构,如查找,增加,删除等操作。
在头文件中添加
1.
#include
2.
typedef
std::deque DEQUELVITEM;
二、打开Visual Studio C++ (6.0),新建工程。(本文的目的是实现自绘 ListCtrl 的,实现过程下面会详细介绍)
a. 首先,生成一个新类名为CMyListCtrl. 其基类为CListCtrl. 这部分工作用ClassWizard很容易完成。
b. 添加相关消息及处理函数,OnPaint() ;OnMouseMove();OnHScroll();OnVScroll等,这部工作用ClassWizard同样很容易完成。编译通过后,接着往下看。
c. 在.h文件顶部定义用户信息结构struct LUSERITEM
d. 在.h文件顶部定义一些常量标志
1.
#define TVS_VIDEO 0x00000001 //有视频设备标志
2.
#define TVS_MOBILEMSG 0x00000002 //可用手机SMS标志
3.
#define TVS_NETDISK 0x00000004
4.
#define TVS_LEADER 0x00000008
5.
#define TVS_VICELEADER 0x00000010
6.
#define TVS_ONLINEUSER 0x00000020
e.添加成员变量 及并在构造函数中初始化
01.
CFont* m_pFont;
//用于创建选择字体
02.
BOOL
m_bOverImage;
03.
BOOL
m_bOverVedio;
04.
BOOL
m_bOverMobile;
05.
DEQUELVITEM m_DequeList;
//用户信息链表
06.
HICON
m_hTailIconA;
//vido flag
07.
HICON
m_hTailIconB;
//mobil message flag
08.
HICON
m_hTailIconC;
09.
HBITMAP
m_hBackBitmap;
//背景
f.添加部分成员函数
重载InsertItem函数,用于增加Item同时增加用户信息。
1.
InsertItem(
int
nItem,
LPCTSTR
szItemText,
int
nImageIndex, LUSERITEM* UserInfo)
2.
{
3.
DEQUELVITEM* pDeqListItem = &m_DequeList;
4.
if
(UserInfo)
5.
pDeqListItem ->push_back(*UserInfo);
6.
nItem = CListCtrl::InsertItem(nItem,szItemText,nImageIndex);
7.
return
nItem;
8.
}
添加设置显示图标函数,A指定视频标志图标,B指定为短消息标志图标,C未定义
01.
void
CMyListCtrl::SetTailIcon(
LPCTSTR
strIconFileA,
LPCTSTR
strIconFileB,
LPCTSTR
strIconFileC)
02.
{
03.
HICON
hIcon=NULL;
04.
hIcon = (
HICON
)::LoadImage(NULL, strIconFileA, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
05.
if
(hIcon)
06.
{
07.
if
(m_hTailIconA)
08.
DeleteObject(m_hTailIconA);
09.
m_hTailIconA = hIcon;
10.
}
11.
12.
hIcon = (
HICON
)::LoadImage(NULL, strIconFileB, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
13.
14.
if
(hIcon)
15.
{
16.
if
(m_hTailIconB)
17.
DeleteObject(m_hTailIconB);
18.
19.
m_hTailIconB = hIcon;
20.
}
21.
22.
hIcon = (
HICON
)::LoadImage(NULL, strIconFileC, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
23.
24.
if
(hIcon)
25.
{
26.
if
(m_hTailIconC)
27.
DeleteObject(m_hTailIconC);
28.
29.
m_hTailIconC = hIcon;
30.
}
31.
}
添加设背景位图函数SetBackBitmap
1.
void
CMyListCtrl::SetBackBitmap(
LPCTSTR
lpszResourceName)
2.
{
3.
HBITMAP
hBmp = (
HBITMAP
)::LoadImageFile(lpszResourceName);
4.
if
(hBmp)
5.
m_hBackBitmap = hBmp;
6.
}
添加删除用户信息函数
01.
BOOL
DeleteUserInfo(CString szText)
02.
{
03.
BOOL
bRet = FALSE;
04.
05.
LUSERITEM itemInfo;
06.
DEQUELVITEM* pDeqItem = &m_DequeList;
07.
08.
int
nItemCount = -1;
09.
10.
DEQUELVITEM::iterator it,itbegin = pDeqItem->begin(),itend = pDeqItem->end();
11.
for
( it = itbegin; it != itend; it++ )
12.
{
13.
nItemCount++;
14.
if
(( it->szUserID == szText)||(it->szUserName == szText))
15.
{
16.
if
( nItemCount == ( pDeqItem->size() - 1 ) )
17.
{
18.
//如果是最后一个
19.
20.
pDeqItem->pop_back();
21.
}
22.
else
if
( nItemCount == 0 )
23.
//如果是第一个
24.
pDeqItem->pop_front();
25.
else
26.
pDeqItem->erase( pDeqItem->begin() + nItemCount );
27.
28.
bRet = TRUE;
29.
}
30.
}
31.
32.
return
bRet;
33.
}
三、自绘代码全部在OnPaint()中实现 ,为了节省篇幅这里省略,请参考源码。
四、结束语
为了让VC程序员编写聊天软件时能够更好地美化其软件界面,本人写了这样CMyListCtrl 并给出了其源码,希望对大家有所启发!CMyTreeCtrl的自绘的实现也是大同小异,有兴趣的可去试试!。有任何问题请和本人联系:song_0962@sina.com (QQ:34544052)
(全文完)