CListCtrl是列表控件類,列表控件的每一行叫做一個item,每一列叫做一個subitem。每一行和每一列都有個ID號,可以確定唯一的單元格。
最近使用了這個控件,有心得總結如下:
(Dialog模式)
1. 創建列表控件.
定義一個成員變量:CListCtrl *m_pListCtrl;
在初始化對話框的時候創建列表,也就是在OnInitDialog()中創建(在哪裏創建沒有規定,只要符合對象和資源創建的規則即可。)
m_pListCtrl=new CListCtrl();
m_pListCtrl->Create();
m_pListCtrl->SetExtendedStyle();
Create函數有四個參數,DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID 。第一個參數可以是一些值的組合,用來定義列表的樣式,這些值MSDN有詳細說明,我只舉一個簡單的例子,更多的查看MSDN。例如第一個參數使用 LVS_REPORT,則表示列表有標題,或者說有列頭,很多其他的樣式必須是和這個樣式搭配使用纔有效。第二個參數是列表的座標和大小,這個矩形框就表示列表的矩形框。第三個參數表示這個列表的父類,如果列表在對話框上或者是在試圖上,這個參數可以是this。第四個參數是列表的資源號。
SetExtendedStyle函數只有一個參數,是一些值的組合,用來設置列表的擴展樣式,這些值MSDN有詳細的說明,我只介紹兩個比較常用的。 LVS_EX_GRIDLINES :列表顯示網格,只適用於LVS_REPORT 風格。LVS_EX_FULLROWSELECT:當一個item被選中時,它的所有subitems也處於被選中狀態,點擊任意一個subitem,則可同時選中整個行. 只適用於LVS_REPORT 風格。
2.構建列表
構建列表也就是兩個操作,插入列和插入行,前面說過,一個列就是一個subitem,一行就是一個item。
m_pListCtrl->InsertColumn(0,"第一列");
列索引從0開始,InsertColumn函數將在列表中建立一個列,如果數據有三列,就調用三次此函數,索引分別是0,1,2。
m_pListCtrl->InsertItem(0,"第一行");
行索引也從0開始,InsertItem函數在列表中建立一行,且“第一行”的字樣顯示在的一列中,其他各列爲空,如果要在除第一列以外的列中插入數據,調用SetItemText()函數,這個函數有三個參數,第一個參數表示要在第幾行插入,第二個參數表示要在第幾列插入,第三個參數表示要插入的內容。
可以設定列寬:
LVCOLUMN lvcolumn;
m_pListCtrl->GetColumn(0,&lvcolumn);
lvcolumn.mask=LVCF_WIDTH;
lvcolumn.cx=335;
m_pListCtrl->SetColumn(0,&lvcolumn);
表示將列寬設定爲335像素。
3.操作列表
這裏主要介紹一個比較有用的函數SubItemHitTest()
當鼠標停留在列表框上時,此函數會返回鼠標所在的行號,和鼠標所在的列號。
LVHITTESTINFO linfo;
DWORD dwPos = GetMessagePos();
CPoint point(LOWORD(dwPos),HIWORD(dwPos));
m_pListCtrl->ScreenToClient(&point);
linfo.pt=point;
linfo.flags=LVHT_ABOVE;
int iItem=m_pListCtrl->SubItemHitTest(&linfo);
行號保存在linfo.iItem中,列號保存在linfo.iSubItem中,此時可以調用GetItemText()函數獲得這個單元格的內容了。
這個方法常用在列表的NM_CLICK和NM_DBLCLK事件中。
此類消息爲:
ON_NOTIFY(NM_CLICK, IDC, OnFun)
函數原型:
void OnFun(NMHDR *pNMHDR, LRESULT *pResult)
{
*pResult = 0;
}
4.列表中的CheckBox
列表有多中風格,帶CheckBox是其中常見的一種,設置這種風格的列表只需要在調用SetExtendedStyle函數的時候設置LVS_EX_CHECKBOXES。對CheckBox最常見的操作就是選中和取消,可能是我的知識貧乏,到目前,我還沒有找到專門的消息處理這兩個操作。要處理這樣的操作,我使用下面的方法。
響應列表的NM_CLICK消息,在這個消息處理函數中,判斷鼠標點擊的位置是文本還是CheckBox,代碼如下:
UINT nFlag;
DWORD dwPos = GetMessagePos();
CPoint point(LOWORD(dwPos),HIWORD(dwPos));
m_pList1->ScreenToClient(&point);
m_pList1->HitTest ( point , &nFlag ) ;
如果nFlag==LVHT_ONITEMSTATEICON ,說明鼠標點中了CheckBox。本可以調用GetCheck函數來獲得CheckBox的狀態,但是很可惜,CheckBox的狀態似乎是在 NM_CLICK消息函數結束以後纔會改變。所以,在NM_CLICK的消息處理函數中獲得CheckBox的狀態是沒有改變之前的。
還有一點需要注意,假設現在目前CheckBox的狀態是選中,那麼,當你點擊兩下鼠標的時候,CheckBox還是選中狀態,而且 NM_CLICK消息處理函數被執行了兩次,所以,結果是正確的。但是當你點擊兩下鼠標時速度比較快,那麼系統會把兩次單擊鼠標看作一次雙擊,這樣,NM_CLICK的消息處理函數只會執行一次,但是,注意但是,CheckBox的狀態卻改變了兩次。因此,這個時候你看到的CheckBox的狀態和程序的結果就相反了。要解決這個問題,就需要在NM_DBLCLK的消息函數中做相應的工作。
以上若有不足之處,希望各位高手指正。
2009/04/01補充:
禁止列表頭調整大小,需要派生一個CListCtrl類,重載一下函數爲:
BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam;
switch (((NMHDR*)lParam)->code)
{
case HDN_BEGINTRACKW:
case HDN_BEGINTRACKA:
case HDN_DIVIDERDBLCLICKA:
case HDN_DIVIDERDBLCLICKW:
*pResult = TRUE;
//
disable tracking
return TRUE;
}
return CListCtrl::OnNotify(wParam, lParam, pResult);
}