程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 讓你的軟件界面更漂亮(六)仿QQ主界面之ListCtrl

讓你的軟件界面更漂亮(六)仿QQ主界面之ListCtrl

編輯:關於VC++

QQ軟件主界面的ListCtrl 是可以說非常經典了,一個字漂亮! 這個ListCtrl的所包含的信息之豐富,更是讓我們這些軟件工程師望Q興歎! 今天,我將和大家一起來寫一個屬於自已的CMyListCtrl。

一、實現CMyListCtrl要完成的任務及實現方法分析。

1.MyListCtrl 顯示彩色圖片頭像(在線用戶頭)

讓CMyListCtrl 顯示彩色圖片作為頭像很容易,用CImageList 加載規格相同的圖片到其中,然後讓CimageList和CMyListCtrl關聯就可實現, 向ImageList 添加圖片或圖標的三種方法代碼總結如下:

CImageList m_imageList;
m_imagelist.Create(40, 40, ILC_MASK|ILC_COLOR32, 1, 1);
//添加ID 為IDI_ICON的圖標
m_imageList.Add( AfxGetApp()->LoadIcon(IDI_ICON));
//從圖標文件中加載並添加
HICON hIcon = (HICON)LoadImage(NULL, ".\\image\\SQQun.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE)
m_imagelist.Add(hIcon);
//從位圖文件中加載並添加
CBitmap *pBitmap=new CBitmap;
pbitmap ->m_hObject = (HBITMAP) LoadImage(NULL, "face.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
m_imagelist.Add(pBitmap, RGB(255,255, 255)/*mask color*/);

Delete pBitmap;

把 CMyListCtrl 和CimageList 關聯並向ListCtrl 添加用戶代碼

//先創建ListCtrl (m_MyFriendListCtrl)
if(m_MyFriendListCtrl.Create(LVS_SMALLICON | WS_TABSTOP|WS_CHILD,
          CRect(50,100,206,180), this, IDD_TALKER_LIST/*ID*/))
{
      //關聯
     m_MyFriendListCtrl.SetImageList(&m_imagelistBig,LVSIL_SMALL);
     m_MyFriendListCtrl.SetBackBitmap(_T(".\\image\\mainFrame-centerMid.bmp"));
     //往ListCtrl 中添加好友
     for(int i=0; i<10; i++)
     {
       CString strTemp;
       strTemp.Format("我的好友%d",i);
       LUSERITEM userInfo;
       userInfo.szUserID = strTemp.GetBuffer(strTemp.GetLength());
       strTemp.ReleaseBuffer();
       userInfo.szNoticeMsg = _T("天不怕!地不怕!");
       m_MyFriendListCtrl.InsertItem(i,strTemp, i,&userInfo);//第三個參數 Index of the ImageList
       m_MyFriendListCtrl.SetItemData(i,(i%2)? i|0x00000020:i);
     }
     m_MyFriendListCtrl.ShowWindow(SW_SHOW);
}

2.讓CMyListCtrl 顯示灰色圖片頭像(非在線用戶)

顯示灰色圖片的方法可用圖像處理軟件處理成單色位圖文件後使用,也可用軟件代碼實現轉換,前者的方法處理位圖文件個數不多是還行,否則就顯得麻煩了,其優點運行速度快。用軟件代碼轉換的方法也是可行的,象LoadImage ()或CopyImage()API都可實現,但要占用很多CUP時間。對比兩種方法,我選擇後者,原因不用說我想大家也清楚。

實現彩色圖片到單色位圖轉換的方法是先獲取CMyListCtrl的ImageList 並提取ListCtrl中的Item 對應的圖像後,用代碼轉換成單色位圖並在原位置顯示。轉換過程如下:

CImageList* pImageList=NULL;
pImageList = GetImageList(LVSIL_SMALL);
if(pImageList !=NULL)
{
   HICON hIcon=NULL;         
   hIcon = pImageList->ExtractIcon(nItem);
   HBITMAP hbitmap,hBitmapMask; 
   ICONINFO* iconinfo = new ICONINFO;          
   if(::GetIconInfo(hIcon, iconinfo))
   {
     hbitmap  =  iconinfo->hbmColor;          
     hBitmapMask = iconinfo->hbmMask;
     if (!(nStyle & TVS_ONLINEUSER))
       //hbitmap = BitmapColorToGray(m_hDll,&memDC,hbitmap);            
       hbitmap = (HBITMAP) CopyImage(hbitmap, IMAGE_BITMAP,0, 0,LR_COPYDELETEORG|LR_MONOCHROME);
     DrawBitmap(m_hDll, &memDC,hbitmap,rcIcon);
     DeleteObject(hbitmap);
     DeleteObject(hBitmapMask);
   }
   delete iconinfo;
   ::DestroyIcon(hIcon);
}

實現彩色圖片到單色位圖轉換的語句為:

hbitmap = (HBITMAP) CopyImage(hbitmap, IMAGE_BITMAP,0, 0,LR_COPYDELETEORG|LR_MONOCHROME);

我也寫了一個實現彩色圖片到單色位圖轉換算法,其代碼如下:

//這是本人寫的一個轉換算法。效果好,但運行時間稍長
HBITMAP BitmapColorToGray(CDC* pDC,HBITMAP hBitmap)
{
    BITMAP bmpInfo;
    ::GetObject(hBitmap,sizeof(BITMAP),&bmpInfo);
    if(pDC)
    {
       CDC memDC;
       if( !memDC.CreateCompatibleDC(pDC) )
       {
          return NULL;
       }
       HBITMAP oldBitmap = (HBITMAP)memDC.SelectObject(hBitmap);
       DWORD  r,g,b; 
       for (int H =0; H <= bmpInfo.bmHeight; H++)
       {
          for(int W = 0; W <= bmpInfo.bmWidth; W ++)
          { 
             r = GetRValue(memDC.GetPixel(W,H)); 
             g = GetGValue(memDC.GetPixel(W,H)); 
             b = GetBValue(memDC.GetPixel(W,H)); 
             r = (r * 3 + g * 6 + g) / 10; 
             g  =  r; 
             b  =  g; 
             memDC.SetPixel(CPoint(W,H),RGB(r,g,b));
          }
       }
       hBitmap = (HBITMAP)memDC.SelectObject(oldBitmap);
       memDC.DeleteDC();
    }
   return hBitmap;     
}

3.MyListCtrl 要包含豐富的用戶信息(如 ID,NAME 、IP Address 、視頻可用 ,手機短消息等用戶信息)

QQ 的ListCtrl 包含了很多信息,如在線用戶和不在線用戶的頭象不同,有視頻設備的用戶還會顯示標志,開通了手機短消息功能的也會顯示標志,等等。這是如何實現的?找MSDN分析CListCtrl 發現,有兩個函數SetItemData(int nItem,DWORD dwData),和

DWORD GetItemData(int nItem),非常有用,這個32位 data 做幾個標志還是不錯的,但還是無法表達更多的東東。如果把這32位 data作為外部結構的地址是否可行呢?經實驗是可行的,但在要外部處理,封裝性能不好! 於是定義了一個用戶信息的結構。

struct LUSERITEM
{
   CString     szUserID;
   CString     szUserName;
   CString     szIPAddress;
   CString     szServerAddress;
   CString     szNoticeMsg;
   BOOL      bOnline;
   int       nHeadImageIndex;
   //根據需要可增加信息
};

再定義一個鏈表,用來管理用戶信息的結構,如查找,增加,刪除等操作。 在頭文件中添加

#include <dequ>
typedef std::deque<LUSERITEM> DEQUELVITEM;

二、打開Visual Studio C++ (6.0),新建工程。(本文的目的是實現自繪 ListCtrl 的,實現過程下面會詳細介紹)

a. 首先,生成一個新類名為CMyListCtrl. 其基類為CListCtrl. 這部分工作用ClassWizard很容易完成。

b. 添加相關消息及處理函數,OnPaint() ;OnMouseMove();OnHScroll();OnVScroll等,這部工作用ClassWizard同樣很容易完成。編譯通過後,接著往下看。

c. 在.h文件頂部定義用戶信息結構struct LUSERITEM

d. 在.h文件頂部定義一些常量標志

#define    TVS_VIDEO       0x00000001 //有視頻設備標志
#define    TVS_MOBILEMSG     0x00000002 //可用手機SMS標志
#define    TVS_NETDISK      0x00000004
#define    TVS_LEADER      0x00000008
#define    TVS_VICELEADER    0x00000010
#define    TVS_ONLINEUSER    0x00000020

e.添加成員變量 及並在構造函數中初始化

CFont* m_pFont;     //用於創建選擇字體
BOOL m_bOverImage;   
BOOL m_bOverVedio;   
BOOL m_bOverMobile;
DEQUELVITEM m_DequeList; //用戶信息鏈表
HICON m_hTailIconA;        //vido flag
HICON m_hTailIconB;     //mobil message flag
HICON m_hTailIconC;     
HBITMAP m_hBackBitmap;   //背景

f.添加部分成員函數

重載InsertItem函數,用於增加Item同時增加用戶信息。

InsertItem(int nItem, LPCTSTR szItemText, int nImageIndex, LUSERITEM* UserInfo)
{
   DEQUELVITEM* pDeqListItem = &m_DequeList;
   if(UserInfo)
     pDeqListItem ->push_back(*UserInfo);
   nItem = CListCtrl::InsertItem(nItem,szItemText,nImageIndex);
   return nItem;
}

添加設置顯示圖標函數,A指定視頻標志圖標,B指定為短消息標志圖標,C未定義

void CMyListCtrl::SetTailIcon(LPCTSTR strIconFileA,LPCTSTR strIconFileB,LPCTSTR strIconFileC)
{
  HICON hIcon=NULL;
  hIcon = (HICON)::LoadImage(NULL, strIconFileA, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
  if(hIcon)
  {
     if(m_hTailIconA)
        DeleteObject(m_hTailIconA);
     m_hTailIconA = hIcon;
   }
  hIcon = (HICON)::LoadImage(NULL, strIconFileB, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
  if(hIcon)
  {
     if(m_hTailIconB)
        DeleteObject(m_hTailIconB);
     m_hTailIconB = hIcon;
   }
  hIcon = (HICON)::LoadImage(NULL, strIconFileC, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE);
  if(hIcon)
  {
     if(m_hTailIconC)
        DeleteObject(m_hTailIconC);
     m_hTailIconC = hIcon;
  }
}

添加設背景位圖函數SetBackBitmap

void CMyListCtrl::SetBackBitmap(LPCTSTR lpszResourceName)
{
  HBITMAP hBmp = (HBITMAP)::LoadImageFile(lpszResourceName);
  if(hBmp)
     m_hBackBitmap = hBmp;
}

添加刪除用戶信息函數

BOOL DeleteUserInfo(CString szText)
{
  BOOL bRet = FALSE;
  LUSERITEM itemInfo;
  DEQUELVITEM* pDeqItem = &m_DequeList;
  int nItemCount = -1;
  DEQUELVITEM::iterator it,itbegin = pDeqItem->begin(),itend = pDeqItem->end();
  for ( it = itbegin; it != itend; it++ )
  {
     nItemCount++;
     if(( it->szUserID == szText)||(it->szUserName == szText))
     {
        if ( nItemCount == ( pDeqItem->size() - 1 ) )
        {
           //如果是最後一個
           pDeqItem->pop_back();
        }
        else if ( nItemCount == 0 )
           //如果是第一個
           pDeqItem->pop_front();
        else
           pDeqItem->erase( pDeqItem->begin() + nItemCount );
        bRet = TRUE;
      }
  }
  return bRet;
}

三、自繪代碼全部在OnPaint()中實現 ,為了節省篇幅這裡省略,請參考源碼。

四、結束語

為了讓VC程序員編寫聊天軟件時能夠更好地美化其軟件界面,本人寫了這樣CMyListCtrl 並給出了其源碼,希望對大家有所啟發!CMyTreeCtrl的自繪的實現也是大同小異,有興趣的可去試試!。有任何問題請和本人聯系:[email protected] (QQ:34544052)

(全文完)

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved