让你的软件界面更漂亮(六) -- 仿QQ主界面之ListCtrl

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)

 

(全文完)

原文地址:http://www.vckbase.com/index.php/wv/1490.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值