讓你的軟件界面更漂亮(六) -- 仿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的自繪的實現也是大同小異,有興趣的可去試試!。有任何問題請和本人聯繫:[email protected] (QQ:34544052)

 

(全文完)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章