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

       續第一篇:MFC總結之CListCtrl用法及技巧(一) http://blog.csdn.net/zwgdft/article/details/7560592html

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

 

 六、禁止拖動表頭

       重載OnNotify消息響應函數,屏蔽兩個消息通知碼:HDN_BEGINTRACKW 和HDN_DIVIDERDBLCLICKW。示例以下:函數

 

 

[cpp]  view plain copy
 
  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. }  

 

 

 七、讓第一列居中顯示

        在插入列時,咱們能夠經過參數nFormat來設置文本居中顯示,可是這種設置對於第一列是沒有做用的。這時咱們能夠考慮將咱們的內容從第二列開始插入(設置爲居中顯示)。先插入第一列,而後刪除第一列,這樣原先的第二列就充當了第一列。字體

 

 

 

 八、設置行高和字體

        設置CListCtrl的行高沒有函數接口,能夠經過自繪來實現,可是比較麻煩。有一個比較簡單的方法是經過使用一個空白的圖像將行撐起來,使其高度發生變化。示例以下:spa

 

 

[cpp]  view plain copy
 
  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 掉建立的字體,避免內存泄露。.net

 

 

[cpp]  view plain copy
 
  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. }  

 

 

 

 九、虛擬列表技術

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

 

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

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

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

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

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

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

 

[cpp]  view plain copy
 
  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. }  

 

 

 

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

         系統經過發送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則在點擊列表頭時響應的消息處理函數中進行設置。

示例以下:

 

[cpp]  view plain copy
 
  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. }  

 

 

 

 十一、向上與向下移動

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

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

② 獲取第nItem行的全部子項內容;

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

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

示例以下:

 

[cpp]  view plain copy
 
  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. }  

 

 

 

 十二、避免閃爍問題

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

 

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

 

1三、動態調整大小

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

                                              寬度 = 左上角View寬度

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

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

相關文章
相關標籤/搜索