MFC總結之CListCtrl用法及技巧(二)

本篇重點介紹:止拖動表頭、讓第一列居中顯示、設置行高與字體、虛擬列表技術、點擊表頭時進行歸類、向上與向下移動、動態調整大小問題、避免閃爍問題


 6、禁止拖動表頭

       重載OnNotify消息響應函數,屏蔽兩個消息通知碼:HDN_BEGINTRACKWHDN_DIVIDERDBLCLICKW。示例如下:
  1. BOOL CXXXX::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)  
  2. {  
  3.     // TODO: Add your specialized code here and/or call the base class  
  4.     //屏蔽兩個消息通知碼,使得禁止拖動List表頭  
  5.     NMHEADER* pNMHeader = (NMHEADER*)lParam;  
  6.     if(((pNMHeader->hdr.code == HDN_BEGINTRACKW) |   
  7.              (pNMHeader->hdr.code == HDN_DIVIDERDBLCLICKW)))  
  8.     {  
  9.         *pResult = TRUE;  
  10.         return TRUE;  
  11.     }  
  12.   
  13.     return CDialog::OnNotify(wParam, lParam, pResult);  
  14. }  

 7、讓第一列居中顯示

        在插入列時,我們可以通過參數nFormat來設置文本居中顯示,但是這種設置對於第一列是沒有作用的。這時我們可以考慮將我們的內容從第二列開始插入(設置爲居中顯示)。先插入第一列,然後刪除第一列,這樣原先的第二列就充當了第一列。


 8、設置行高和字體

        設置CListCtrl的行高沒有函數接口,可以通過自繪來實現,但是比較麻煩。有一個比較簡單的方法是通過使用一個空白的圖像將行撐起來,使其高度發生變化。示例如下:
  1. CImageList m_image;  
  2. m_image.Create(1,24,ILC_COLOR32,1,0);  
  3. m_listInfo.SetImageList(&m_image, LVSIL_SMALL);  
        對於字體的設置,我們可以使用SetFont函數來實現。以修改CListView的字體爲例,在OnInitialUpdate函數中插入列之前調用SetFontSelf函數(該函數自定義,如下示例所示)。首先創建一個字體,然後調用SetFont進行設置。需要注意的是,在退出時需要delete 掉創建的字體,避免內存泄露。
  1. //設置字體和大小  
  2. void CMyListView::SetFontSelf(int nHeight, LPCTSTR lpszFacename)  
  3. {  
  4.     //先刪除原有字體  
  5.     if(m_font != NULL)  
  6.         delete m_font;  
  7.     m_font = new CFont;  
  8.     //創建字體  
  9.     m_font->CreateFont(  
  10.         nHeight,                   // nHeight  
  11.         0,                         // nWidth  
  12.         0,                         // nEscapement  
  13.         0,                         // nOrientation  
  14.         FW_NORMAL,                 // nWeight  
  15.         FALSE,                     // bItalic  
  16.         FALSE,                     // bUnderline  
  17.         0,                         // cStrikeOut  
  18.         ANSI_CHARSET,              // nCharSet  
  19.         OUT_DEFAULT_PRECIS,        // nOutPrecision  
  20.         CLIP_DEFAULT_PRECIS,       // nClipPrecision  
  21.         DEFAULT_QUALITY,           // nQuality  
  22.         DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily  
  23.         lpszFacename);             // lpszFacename  
  24.   
  25.     //設置字體  
  26.     CListCtrl &theCtrl = GetListCtrl();     //獲取控制權,引用變量  
  27.     theCtrl.SetFont(m_font, TRUE);  
  28. }  



 9、虛擬列表技術

        給一個鏈接,介紹的比較詳細:http://hi.baidu.com/qi_xian/blog/item/929b04ce27d02c0592457ef8.html

       當數據量大時,使用InsertItem插入數據的過程是很漫長的。這時我們有兩個方法來解決該問題:一是使用CListCtrl的虛擬列表技術,二是採用分頁顯示的方法。對於虛擬列表技術,上述鏈接中的文章講的很詳細,我用過它的比較簡單的方法,後來改用了分頁方法。

       使用虛擬列表技術,有三點需要搞清楚:

使用虛擬技術時,需要將CListCtrl控件的Owner Data屬性設置爲ture。

給虛擬列表添加元素時,不需要使用InserItem函數,通過調用SetItemCount設置數據總個數,然後由系統產生不同的消息,在相應的消息響應函數中完成插入工作。

虛擬列表向父窗口發送的消息有三種: ⑴ 當它需要數據時,發送LVN_GETDISPINFO消息; ⑵ 當用戶試圖查找某個元素時,發送LVN_ODFINDITEM消息; ⑶當需要緩衝數據時,發送 LVN_ODCACHEHINT消息。    

        當我們使用LVN_GETDISPINFO 的消息處理函數來插入元素時,必須首先檢查列表請求的是什麼數據(如LVIF_TEXT、LVIF_IMAGE等),然後插入不同的子項。示例如下:

  1. void CDataAnalysis::OnLvnGetdispinfoAnalysisList(NMHDR *pNMHDR, LRESULT *pResult)  
  2. {  
  3.     NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);  
  4.     // TODO: Add your control notification handler code here  
  5.     LV_ITEM* pItem= &(pDispInfo)->item;  
  6.     int iItemIndex= pItem->iItem;  
  7.     size_t converted = 0;  
  8.     wchar_t wStr[30];            //Unicode字符串  
  9.     if (pItem->mask & LVIF_TEXT) //字符串緩衝區有效  
  10.     {  
  11.         switch(pItem->iSubItem)  
  12.         {  
  13.         case 0: //填充數據項的名字,xxxxx表示要填充的字符  
  14.             mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);  
  15.             lstrcpy(pItem->pszText,wStr);  
  16.             break;  
  17.         case 1: //填充子項1  
  18.             mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);  
  19.             lstrcpy(pItem->pszText,wStr);  
  20.             break;  
  21.         case 2: //填充子項2  
  22.             mbstowcs_s(&converted, wStr, 30, xxxxxx, _TRUNCATE);  
  23.             lstrcpy(pItem->pszText,wStr);  
  24.             break;  
  25.         case 3: //填充子項3  
  26.             lstrcpy(pItem->pszText,xxxxxx);  
  27.             break;  
  28.         }  
  29.     }  
  30.   
  31.     *pResult = 0;  
  32. }  



 10、點擊表頭時進行歸類排序

         系統通過發送LVM_SORTITEMS消息來處理歸類問題,在該消息的處理函數中需要調用一個回調函數,這個回調函數需要我們來設計,以完成不同的歸類方法。回調函數原型如下:

                      int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)

          針對上述回調函數,有以下幾點需要搞清楚:

對於參數lparam1和lparam2,分別爲CListCtrl的兩行數據,是用於比較的對象。通過CListCtrl的成員函數SetItemData來設置,該函數原型:

                     int SetItemData(int nIndex,  DWORD_PTR dwItemData )

其第一個參數爲行號,第二個參數指明瞭該行對應的參數。參數dwItemData 通常設爲一行參數的數組,如: pData[2][2] = {{1, 3},{2, 3}}; 每次使用pData[i]作爲dwItemData。

對於參數lParamSort,用於指明列項,即第幾列。該參數和回調函數一同通過CListCtrl的成員函數SortItems來設置,其函數原型爲:

                    BOOL SortItems( PFNLVCOMPARE pfnCompare,DWORD_PTR dwData )

參數 pfnCompare 爲回調函數入口地址, 參數dwData 爲列項。

③ SetItemData在初始插入數據時進行調用來設置,SortItems則在點擊列表頭時響應的消息處理函數中進行設置。

示例如下:

  1. //初始化列表視圖控件  
  2. BOOL CDataAnalysis::InitListCtl()  
  3. {  
  4.     //其他處理,包括設置風格,插入列等等  
  5.     //插入行  
  6.     for(int i=0; i<LineNum; i++)  
  7.     {  
  8.         //要將char*轉換爲wchar_t*  
  9.         mbstowcs_s(&converted, wStr, 30, m_analysis[i].Date, _TRUNCATE);  
  10.         m_listAnalysis.InsertItem(i, wStr);                             //日期  
  11.         mbstowcs_s(&converted, wStr, 30, m_analysis[i].Time, _TRUNCATE);  
  12.         m_listAnalysis.SetItemText(i, 1, wStr);                         //時間  
  13.         mbstowcs_s(&converted, wStr, 30, m_analysis[i].ID, _TRUNCATE);  
  14.         m_listAnalysis.SetItemText(i, 2, wStr);                         //ID  
  15.         m_listAnalysis.SetItemText(i, 3, m_analysis[i].lpszEvent);      //事件  
  16.   
  17.         //設置回調函數的參數  
  18.         m_listAnalysis.SetItemData(i, (LPARAM)(m_analysis+i));  
  19.     }  
  20.   
  21.     return TRUE;  
  22. }  
  23. void CDataAnalysis::OnHdnItemclickAnalysisList(NMHDR *pNMHDR, LRESULT *pResult)  
  24. {  
  25.     LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);  
  26.     // TODO: Add your control notification handler code here  
  27.   
  28.     //設置回調函數的參數和入口地址  
  29.     m_listAnalysis.SortItems(SortFunc, phdr->iItem);  
  30.   
  31.     *pResult = 0;  
  32. }  
  33. //排序的回調函數  
  34. int CALLBACK SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)  
  35. {  
  36.     int result;     //返回值  
  37.   
  38.     //兩行的參數,用於比較  
  39.     ANALYSISFORMAT* pAnalysis1 = (ANALYSISFORMAT*)lParam1;  
  40.     ANALYSISFORMAT* pAnalysis2 = (ANALYSISFORMAT*)lParam2;  
  41.   
  42.     //排序  
  43.     switch(lParamSort)  
  44.     {  
  45.     case 0:     //日期  
  46.         result = strcmp(pAnalysis1->Date, pAnalysis2->Date);  
  47.         break;  
  48.     case 1:     //時間  
  49.         result = strcmp(pAnalysis1->Time, pAnalysis2->Time);  
  50.         break;  
  51.     case 2:     //ID  
  52.         result = strcmp(pAnalysis1->ID, pAnalysis2->ID);  
  53.         break;  
  54.     case 3:     //事件  
  55.         result = wcscmp(pAnalysis1->lpszEvent, pAnalysis2->lpszEvent);  
  56.         break;  
  57.     default:  
  58.         break;  
  59.     }  
  60.   
  61.     return result;  
  62. }  

 11、向上與向下移動

        有時需要向上或向下移動表項內容,這裏給出向上移動的方法,向下移動的方法類似。

① 利用第2節所述的內容獲取行號nItem,判斷行號是否爲行首,如果不是行首則進入②;

② 獲取第nItem行的所有子項內容;

③ 刪除第nItem行,並在nItem-1的位置重新插入原先的第nItem行的內容;

④ 使nItem-1的位置高亮顯示

示例如下:

  1. /*************************上移子項**************************/  
  2. void CStudyTestDlg::OnPageup()   
  3. {  
  4.     if (nItem == 0)  
  5.     {  
  6.         MessageBox("該子項已經位於第一行!");  
  7.         return;  
  8.     }  
  9.   
  10.     // 提取內容  
  11.     CString temp[4];  
  12.     int i;  
  13.     for(i=0;i<4;i++)  
  14.         temp[i] = m_ListCtrl.GetItemText(nItem, i);  
  15.   
  16.     // 刪除  
  17.     m_ListCtrl.DeleteItem(nItem);  
  18.   
  19.     // 在nItem-1位置處插入  
  20.     for (i=0; i<4; i++)  
  21.         m_ListCtrl.SetItemText(nItem-1,i,temp[i]);  
  22.   
  23.     //高亮顯示  
  24.     UINT flag = LVIS_SELECTED|LVIS_FOCUSED;  
  25.     m_ListCtrl.SetItemState(--nItem, flag, flag);  
  26. }  
  27.   
  28. /*************************下移子項**************************/  
  29. void CStudyTestDlg::OnPagedown()   
  30. {  
  31.     if (nItem == m_ListCtrl.GetItemCount()-1)  
  32.     {  
  33.         MessageBox("該子項已經位於最後一行!");  
  34.         return;  
  35.     }  
  36.   
  37.     // 提取內容  
  38.     CString temp[4];  
  39.     int i;  
  40.     for (i=0; i<4; i++)  
  41.         temp[i] = m_ListCtrl.GetItemText(nItem, i);  
  42.   
  43.     // 刪除  
  44.     m_ListCtrl.DeleteItem(nItem);  
  45.   
  46.     // 在nItem+1位置處插入  
  47.     for (i=0; i<4; i++)  
  48.         m_ListCtrl.SetItemText(nItem+1, i,temp[i]);  
  49.   
  50.     //高亮顯示  
  51.     UINT flag = LVIS_SELECTED|LVIS_FOCUSED;  
  52.     m_ListCtrl.SetItemState(++nItem, flag, flag);  
  53. }  

 12、避免閃爍問題

           這個問題在我的前面一篇博文有提到。

  http://blog.csdn.net/zwgdft/article/details/7394318

 

13、動態調整大小

        有時由於不確定軟件運行時的電腦屏幕大小,需要根據屏幕大小動態設置CListCtrl控件的大小。動態大小的設置時,需要注意不要將高度和寬度設置的超過區域限制,否則就沒有滾動條了,導致部分內容無法查看。以我遇到的一個例子來說,其情況見第12節提到的那篇博文所述:將View劃分爲三個窗格,在左上角View上有個CPropertySheet,其上有幾個CPropertyPage,每個屬性頁上有個CListCtrl,供用戶查看信息。那麼這時需要設置的CListCtrl的大小即爲:

                                              寬度 = 左上角View寬度

                                              高度 = 左上角View高度 - 屬性頁的Tab項高度

調用MoveWindow函數進行設置即可。


------------------全文完--------------------

發佈了286 篇原創文章 · 獲贊 57 · 訪問量 118萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章