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)
(全文完)