1. 程式人生 > 實用技巧 >list control總結1

list control總結1

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

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

本文根據本人在專案中的應用,來談談CListCtrl的部分用法及技巧。當初學習時,查了很多資料,零零碎碎的作了些記錄,現在主要是來做個總結,方便以後查閱。主要包括以下十三點內容:基本操作、獲取選中行的行號、複選框操作、動態設定選中行的字型顏色、設定選中行的背景顏色、禁止拖動表頭、讓第一列居中顯示、設定行高與字型、虛擬列表技術、點選表頭時進行歸類、向上與向下移動、動態調整大小問題、避免閃爍問題。

分為兩篇來進行總結。本篇重點總結:基本操作獲取選中行的行號

複選框操作動態設定選中行的字型顏色設定選中行的背景顏色


1、基本操作

分別從下面四點來介紹CListCtrl的基本操作:

①設定列表檢視顯示方式

Ⅰ.CListCtrl有四種樣式:LVS_ICON、LVS_SMALLICON、LVS_LIST、LSV_REPORT,可通過控制元件屬性來設定。本文所述均為LSV_REPORT屬性。

Ⅱ.擴充套件樣式:

常用的擴充套件樣式有三種:LVS_EX_FULLROWSELECT、LVS_EX_GRIDLINES、LVS_EX_CHECKBOXES,分別對應作用 選中某行時使正行高亮、設定網格線、item前生成Ckeckbox控制元件。

使用SetExtendedStyle(style)函式設定擴充套件樣式,使用GetExtendedStyle()函式獲取樣式,如:

m_listInfo.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
Ⅲ.使用CListView時,需要在PreCreateWindow()函式中新增 cs.style | = LVS_REPORT;

來將其設定為LVS_REPORT風格,否則插入無效。還用另一種方法來設定風格,即在OnInitialUpate()中獲取CListCtrl控制權,然後修改風格,如下所示:

CListCtrl &theCtrl =GetListCtrl

();

theCtrl.ModifyStyle(0, LVS_REPORT);

②插入操作

先插入列:

intInsertColumn( intnCol, LPCTSTRlpszColumnHeading, intnFormat, intnWidth, intnSubItem)

插入列時,可指明列號、列名稱、列名稱顯示樣式,列寬等資訊。對於列號為0的那一列,始終是靠左顯示,後面會有修改使其劇中顯示的方法,其他列通過設定nFormat屬性可以居中顯示。

插入行:

intInsertItem( intnItem, LPCTSTRlpszItem)

直接插入一行,nItem指明行號,lpszItem指明該行第0列的資訊。

設定資訊:

BOOLSetItemText(int nItem, int nSubItem, LPCTSTR lpszText )

設定第nItem行nSubItem列的資訊(nItem:0,1,2,3……; nSubItem:1,2,3……)

③刪除操作

有三個操作函式:

BOOLDeleteAllItems() -------刪除所有的行

BOOLDeleteItem(nItem) --------刪除某一行

BOOLDeleteColumn(nCol) -----刪除某一列

④獲取/設定屬性函式

有很多函數了,就不一一介紹了。常用的有

intGetItemCount() -------- 獲取已插入資訊的行數

BOOLSetItemState(intiLink, UINTstate, UINTstateMask) ---------設定行狀態,如高亮顯示等

等等


2、獲取選中行的行號

獲取選中行的行號,然後對該行進行相關處理,這點在程式設計中用的非常多。

當滑鼠單擊item時,控制元件向父視窗傳送NM_CLICK訊息,其響應函式為OnNMClickXXXX(NMHDR *pNMHDR, LRESULT *pResult),在該函式下來編寫程式碼獲取滑鼠點選的行號。

有兩種方法來獲取行號:第一種是使用GetFirstSelectedItemPositionGetNextSelectedItem配合來獲取;第二種是先獲取滑鼠位置資訊,然後呼叫HitTest函式來找出行號。示例分別如下:

第一種方法,該示例截自MSDN,可作修改後使用。

[cpp] view plain copy print ?
  1. POSITIONpos=pList->GetFirstSelectedItemPosition();
  2. if(pos==NULL)
  3. TRACE0("Noitemswereselected!\n");
  4. else
  5. {
  6. while(pos)
  7. {
  8. intnItem=pList->GetNextSelectedItem(pos);
  9. TRACE1("Item%dwasselected!\n",nItem);
  10. //youcoulddoyourownprocessingonnItemhere
  11. }
  12. }
第二種方法,該示例來自我的專案,可作修改後使用。
[cpp] view plain copy print ?
  1. //獲取單擊所在的行號
  2. //找出滑鼠位置
  3. DWORDdwPos=GetMessagePos();
  4. CPointpoint(LOWORD(dwPos),HIWORD(dwPos));
  5. m_listCtrl.ScreenToClient(&point);
  6. //定義結構體
  7. LVHITTESTINFOlvinfo;
  8. lvinfo.pt=point;
  9. //獲取行號資訊
  10. intnItem=m_listCtrl.HitTest(&lvinfo);
  11. if(nItem!=-1)
  12. m_itemSel=lvinfo.iItem;//當前行號
對於LVHITTESTINFO 結構體,其有四個成員,在上述HitTest呼叫中,其第一個成員作為輸入,另外三個作為輸出。具體變數含義可檢視MSDN。 [cpp] view plain copy print ?
  1. typedefstruct_LVHITTESTINFO{
  2. POINTpt;
  3. UINTflags;
  4. intiItem;
  5. intiSubItem;
  6. }LVHITTESTINFO,*LPLVHITTESTINFO;

3複選框操作

有時需要在item前面新增一個CheckBox,供使用者選擇,然後對所有選中項進行處理。

這裡涉及到兩個問題:第一個,如何新增CheckBox風格;第二個,如何判斷某一行的CheckBox狀態是否發生改變。

對於第一個問題,在基本操作裡已經有所闡述了,即通過SetExtendedStyle函式新增LVS_EX_CHECKBOXES擴充套件風格。

這裡重點探討第二個問題,首先,操作複選框狀態的有兩個函式:

BOOLGetCheck(int nItem)-------獲取複選框狀態

BOOLSetCheck( int nItem, BOOL fCheck = TRUE )-------設定複選框狀態

其次,我們要搞清楚以下四點:

當列表的項item改變時,控制元件會向父視窗傳送LVN_ITEMCHANGED訊息,因此可以在LVN_ITEMCHANGED訊息的響應函式中對複選框的狀態進行處理(查詢或設定)。

滑鼠點選CheckBox時,訊息的順序是 NM_CLICK —> LVN_ITEMCHANGED,即CheckBox的狀態是在 NM_CLICK訊息函式結束後才會發生變化,在NM_CLICK中使用GetCheck無效。

滑鼠點選Item(非CheckBox區域)時,訊息的順序是 LVN_ITEMCHANGED —> NM_CLICK。

呼叫InsertItem 函式時,也會產生LVN_ITEMCHANGED訊息。鑑於此,通常會自定義一個BOOL型變數m_bHit 來判斷是點選操作還是插入操作,該變數初始賦FALSE,當有滑鼠點選item時賦TRUE, 檢測完是否有CheckBox被點選後重新復位為FALSE。

示例如下所示:

[cpp] view plain copy print ?
  1. voidCXXXX::OnNMClickXXXX(NMHDR*pNMHDR,LRESULT*pResult)
  2. {
  3. //獲取單擊所在的行號
  4. //找出滑鼠位置
  5. DWORDdwPos=GetMessagePos();
  6. CPointpoint(LOWORD(dwPos),HIWORD(dwPos));
  7. m_listCtrl.ScreenToClient(&point);
  8. //定義結構體
  9. LVHITTESTINFOlvinfo;
  10. lvinfo.pt=point;
  11. //獲取行號資訊
  12. intnItem=m_listCtrl.HitTest(&lvinfo);
  13. if(nItem!=-1)
  14. m_itemSel=lvinfo.iItem;//當前行號
  15. //判斷是否點選在CheckBox上
  16. if(lvinfo.flags==LVHT_ONITEMSTATEICON)
  17. m_bHit=TRUE;
  18. *pResult=0;
  19. }
  20. voidCXXXX::OnLvnItemchangedXXXX(NMHDR*pNMHDR,LRESULT*pResult)
  21. {
  22. LPNMLISTVIEWpNMLV=reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
  23. //判斷m_bHit,即是否點選了CheckBox
  24. if(m_bHit)
  25. {
  26. m_bHit=FALSE;//復位
  27. if(m_listCtrl.GetCheck(m_itemSel))
  28. {//CheckBox被選中
  29. //doyourownprocessing
  30. }
  31. else
  32. {//CheckBox取消選擇
  33. //doyourownprocessing
  34. }
  35. }
  36. *pResult=0;
  37. }


4、動態設定選中行的字型顏色

有時可能需要設定某行的文字為特殊顏色,以表示某種特殊含義,比如正在下載的資訊用綠色,暫停下載的用灰色。

首先,給出一個CodeProject的連結,這篇文章講的非常好,主要是利用Custom Draw。http://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra

然後,來談談我的方法,這裡主要談對選中行的字型顏色進行動態修改,當然也是我通過上面文章和自己實踐結合得出的。

我們需要搞清楚以下幾點(可以結合下面修改某一行的字型顏色的方法來看):

① 當控制元件繪製時,會發送NM_CUSTOMDRAW訊息,該訊息的訊息響應函式為

[cpp] view plain copy print ?
  1. voidCXXXX::OnNMCustomdrawXXXX(NMHDR*pNMHDR,LRESULT*pResult)
  2. {
  3. LPNMLVCUSTOMDRAWpLVCD=reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
  4. //TODO:Addyourcontrolnotificationhandlercodehere
  5. *pResult=CDRF_DODEFAULT;
  6. //………………
  7. }

②其中,pNMHDR為輸入引數,其指向NMLVCUSTOMDRAW結構體,該結構包含了很多資訊,包括字型顏色、背景等等,特別是第一個成員,為NMCUSTOMDRAW結構體變數,其包含了Current drawing stage(不知道怎麼編譯比較好),其可能的值如下圖(截自MSDN)所示

pResult為輸出引數,該引數決定了接下來向windows傳送什麼訊息(與繪製有關的),通過傳送該訊息我們可以進入下一步需要的處理階段。具體輸出哪個值取決於Current drawing stage,其可能的值如下圖(截自MSDN)所示

④ 有一點必須注意(英文的,我覺得看起來比翻譯過來更精確):

One thing to keep in mind is you must alwayscheck the draw stage before doing anything else, because your handler will receive many messages, and the draw stage determines what action your code takes.

下面我們來看看如何修改某一行的字型顏色:

① 首先,我們應該明白要修改字型顏色,應該在pre-paint 階段來完成

② 因此,在訊息響應函式中,我們首先判斷是否處於pre-paint stage(即pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT),然後通過修改輸出值pResult 的值來通知windows我們需要處理每個item的訊息(即設定 *pResult = CDRF_NOTIFYITEMDRAW)。

③ 再次進入訊息響應函式時,我們判斷是否處於Item的pre-paint stage(即pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT),如果是則進行相關處理,即修改字型顏色等等。

④ 處理完了後重新設定 *pResult = CDRF_DODEFAULT,表示我們不再需要其他特殊的訊息了,預設執行即可。

示例如下:

[cpp] view plain copy print ?
  1. voidCXXXX::OnNMCustomdrawXXXX(NMHDR*pNMHDR,LRESULT*pResult)
  2. {
  3. LPNMLVCUSTOMDRAWpLVCD=reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
  4. *pResult=CDRF_DODEFAULT;
  5. //Firstthing-checkthedrawstage.Ifit'sthecontrol'spre-paintstage,
  6. //thentellWindowswewantmessagesforeveryitem.
  7. if(CDDS_PREPAINT==pLVCD->nmcd.dwDrawStage)
  8. {
  9. *pResult=CDRF_NOTIFYITEMDRAW;
  10. }
  11. elseif(CDDS_ITEMPREPAINT==pLVCD->nmcd.dwDrawStage)
  12. {
  13. //Thisisthenotificationmessageforanitem.
  14. //處理,將item改變背景顏色
  15. if(/*符合條件*/)
  16. pLVCD->clrText=RGB(255,0,255);
  17. *pResult=CDRF_DODEFAULT;
  18. }
  19. }

上面談的方法主要用於設定靜態字型顏色,當然,如果你的列表的資訊在不斷變化(即用SetItemText不斷修改),那麼也就實現了動態改變了,否則需要在合適的地方呼叫重繪函式:

BOOL RedrawItems( int nFirst, int nLast )

表示在nFirst和nLast之間的行需要進行重繪。


5、設定選中行的背景顏色


設定選中行的背景顏色,可以將選中行以特殊顏色顯示,容易明白當前處理的是哪一行。儘管有高亮,但是高亮是基於焦點的,如果你選中了某一行,然後焦點轉移了,這是就無法判斷你選的是哪一行了。

設定選中行的背景顏色的方法和第四節中講的修改字型顏色的方法是相似的,都是利用Custom Draw。這裡涉及到設定當前選中行為特殊顏色,同時要恢復前一次選中行的顏色,否則就亂了。因此需要記錄前一次選中行、當前選中行的行號,相信通過前面的總結,這點並不難實現。然後在當前選中行和前一次選中行之間進行重繪即可。

示例如下:

[cpp] view plain copy print ?
  1. voidCXXXX::OnNMClickXXXX(NMHDR*pNMHDR,LRESULT*pResult)
  2. {
  3. //…………
  4. //重繪item,更改背景顏色
  5. intnFirst=min(m_itemSel,m_itemForeSel);
  6. intnLast=max(m_itemSel,m_itemForeSel);
  7. m_listCtrl.RedrawItems(nFirst,nLast);//在前一次選中的item和當前選中的Item之間進行重繪
  8. *pResult=0;
  9. }
  10. voidCXXXX::OnNMCustomdrawXXXX(NMHDR*pNMHDR,LRESULT*pResult)
  11. {
  12. LPNMLVCUSTOMDRAWpLVCD=reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
  13. *pResult=CDRF_DODEFAULT;
  14. //Firstthing-checkthedrawstage.Ifit'sthecontrol'sprepaint
  15. //stage,thentellWindowswewantmessagesforeveryitem.
  16. if(CDDS_PREPAINT==pLVCD->nmcd.dwDrawStage)
  17. {
  18. *pResult=CDRF_NOTIFYITEMDRAW;
  19. }
  20. elseif(CDDS_ITEMPREPAINT==pLVCD->nmcd.dwDrawStage)
  21. {
  22. //Thisisthenotificationmessageforanitem.
  23. //處理,將item改變背景顏色
  24. if(m_itemSel==pLVCD->nmcd.dwItemSpec)
  25. {//當前選中的item
  26. pLVCD->clrTextBk=RGB(255,0,0);
  27. }
  28. elseif(m_itemForeSel==pLVCD->nmcd.dwItemSpec)
  29. {//前一次選中的item,恢復為白色
  30. pLVCD->clrTextBk=RGB(255,255,255);
  31. }
  32. *pResult=CDRF_DODEFAULT;
  33. }
  34. }

轉載於:https://my.oschina.net/ypimgt/blog/91022