QQ 主介面的 ListCtrl 堪稱經典:彩色頭像、離線灰階、視訊/簡訊狀態圖示、豐富用戶資訊一應俱全。本文將帶領大家從零開始,打造一個功能相當的 CMyListCtrl,並完整解析關鍵技術點。

使用 CImageList 載入同尺寸圖片並關聯至 ListCtrl 即可。向 ImageList 添加圖片/圖示的三種常見方式:
CImageList m_imageList;
m_imagelist.Create(40, 40, ILC_MASK | ILC_COLOR32, 1, 1);
// 1. 添加資源圖示
m_imageList.Add(AfxGetApp()->LoadIcon(IDI_ICON));
// 2. 從 .ico 檔載入
HICON hIcon = (HICON)LoadImage(NULL, _T(".\\image\\SQQun.ico"), IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
m_imagelist.Add(hIcon);
// 3. 從 .bmp 檔載入(指定遮罩色)
CBitmap *pBitmap = new CBitmap;
pBitmap->m_hObject = (HBITMAP)LoadImage(NULL, _T("face.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
m_imagelist.Add(pBitmap, RGB(255, 255, 255));
delete pBitmap;
關聯並添加項目:
if (m_MyFriendListCtrl.Create(LVS_SMALLICON | WS_TABSTOP | WS_CHILD,
CRect(50, 100, 206, 180), this, IDD_TALKER_LIST))
{
m_MyFriendListCtrl.SetImageList(&m_imagelistBig, LVSIL_SMALL);
m_MyFriendListCtrl.SetBackBitmap(_T(".\\image\\mainFrame-centerMid.bmp"));
for (int i = 0; i < 10; i++)
{
CString strTemp;
strTemp.Format(_T("我的好友%d"), i);
LUSERITEM userInfo;
userInfo.szUserID = strTemp.GetBuffer(strTemp.GetLength());
strTemp.ReleaseBuffer();
userInfo.szNoticeMsg = _T("天不怕!地不怕!");
m_MyFriendListCtrl.InsertItem(i, strTemp, i, &userInfo);
m_MyFriendListCtrl.SetItemData(i, (i % 2) ? i | 0x00000020 : i);
}
m_MyFriendListCtrl.ShowWindow(SW_SHOW);
}
灰階轉換可用圖片處理工具預製,亦可運行時以代碼實現。經比較,選用 CopyImage API 配合 LR_MONOCHROME 旗標,效率與效果兼顧:
CImageList* pImageList = GetImageList(LVSIL_SMALL);
if (pImageList != NULL)
{
HICON hIcon = pImageList->ExtractIcon(nItem);
HBITMAP hBitmap, hBitmapMask;
ICONINFO* pIconInfo = new ICONINFO;
if (::GetIconInfo(hIcon, pIconInfo))
{
hBitmap = pIconInfo->hbmColor;
hBitmapMask = pIconInfo->hbmMask;
if (!(nStyle & TVS_ONLINEUSER))
// 關鍵:轉為單色位圖
hBitmap = (HBITMAP)CopyImage(hBitmap, IMAGE_BITMAP, 0, 0,
LR_COPYDELETEORG | LR_MONOCHROME);
DrawBitmap(m_hDll, &memDC, hBitmap, rcIcon);
DeleteObject(hBitmap);
DeleteObject(hBitmapMask);
}
delete pIconInfo;
::DestroyIcon(hIcon);
}
文中也提供了一個像素級灰度演算法(BitmapColorToGray),效果更佳但耗時稍長,供參考。
SetItemData/GetItemData 僅提供 32 位資料,不足以承載複雜資訊。解決方案:定義結構體 LUSERITEM,並以 std::deque 管理:
struct LUSERITEM
{
CString szUserID;
CString szUserName;
CString szIPAddress;
CString szServerAddress;
CString szNoticeMsg;
BOOL bOnline;
int nHeadImageIndex;
// 可按需擴充
};
#include
typedef std::deque DEQUELVITEM;
重載 InsertItem 同步寫入 deque,刪除時同步移除,確保資料一致性。
CMyListCtrl 繼承 CListCtrl。OnPaint、OnMouseMove、OnHScroll、OnVScroll 等訊息處理。TVS_VIDEO、TVS_MOBILEMSG…)、成員變數(字體、懸停旗標、deque、圖示句柄、背景位圖)。InsertItem、SetTailIcon、SetBackBitmap、DeleteUserInfo 等輔助函數。OnPaint() 中(篇幅所限略去,請參考原始碼)。為讓 VC 開發者能快速美化聊天軟體介面,筆者提供完整 CMyListCtrl 原始碼,期望拋磚引玉。CMyTreeCtrl 自繪原理大同小異,有興趣者可自行嘗試。問題歡迎聯繫:song_0962@sina.com (QQ: 34544052)。
