(一)列表控制的主要功能
列表控制和視(List Control&View) 主要用來以各種方式顯示一組數據記錄供用戶進行各種操作,Windows98/95 中資源管理器中的“ 查看” 標籤下的 “ 大圖標|小圖標|列表|詳細資源” 就是一個非常好的典型應用。列表中的記錄可以包括多個數據項,也可以包括表示數據內容的大小圖標,用來表示數據記錄的 各種屬性。
列表控制提供了對Windows 列表功能操作的基本方法,而使用列表視的視函數可以對列表視進行各種操作,通過調用視成員 GetListCtrl 獲取嵌在列表視內列表控制的引用(GetListCtrl& ctrlList = GetListCtrl() ),就可以和列表控制一樣進行各種操作。操作一個列表控制和視的基本方法爲:創建列表控制;創建列表控制所需要的圖像列表;向 列表控制添加表列和表項;對列表進行各種控制,主要包括查找、排序、刪除、顯示方式、排列方式以及各種消息處理功能等;最後撤消列表控制。
對於一個列表控制,其最典型最常用的顯示控制方式爲:大圖標方式(LVS_ICON )、小圖標方式(LVS_SMALLICON )、列表顯示方 式(LVS_LIST )和詳細資料(即報告LVS_REPORT )顯示方式。這可以通過設置其顯示方式屬性來實現。要控制列表所在窗口的風格,可通過功能 函數GetWindowLong 和SetWindowLong 來實現,要控制列表圖標的對齊方式,可通過設置列表窗口的風格LVS_ALIGNTOP 或 LVS_ALIGNLEFT 來實現,
(二)列表控制的對象結構
1 、列表控制的建立方法
CListCtrl &listCtrl 定義列表對象的結構
Create 建立列表控制並綁定對象
列表控制CListCtrl::Create 的調用格式如下:
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );
其中參數dwStyle 用來確定列表控制的風格;rect 用來確定列表控制的大小和位置;pParentWnd 用來確定列表控制的父窗口,通常是一個對話框;nID 用來確定列表控制的標識。其中列表控制的風格可以是下列值的組合:
LVS_ALIGNLEFT 用來確定表項的大小圖標以左對齊方式顯示;
LVS_ALIGNTOP 用來確定表項的大小圖標以頂對齊方式顯示;
LVS_AUTOARRANGE 用來確定表項的大小圖標以自動排列方式顯示;
LVS_EDITLABELS 設置表項文本可以編輯,父窗口必須設有LVN_ENDLABELEDIT 風格;
LVS_ICON 用來確定大圖標的顯示方式;
LVS_LIST 用來確定列表方式顯示;
LVS_NOCOLUMNHEADER 用來確定在詳細資料方式時不顯示列表頭;
LVS_NOLABELWRAP 用來確定以單行方式顯示圖標的文本項;
LVS_NOSCROLL 用來屏蔽滾動條;
LVS_NOSORTHEADER 用來確定列表頭不能用作按鈕功能;
LVS_OWNERDRAWFIXED 在詳細列表方式時允許自繪窗口;
LVS_REPORT 用來確定以詳細資料即報告方式顯示;
LVS_SHAREIMAGELISTS 用來確定共享圖像列表方式;
LVS_SHOWSELALWAYS 用來確定一直顯示被選中表項方式;
LVS_SINGLESEL 用來確定在某一時刻只能有一項被選中;
LVS_SMALLICON 用來確定小圖標顯示方式;
LVS_SORTASCENDING 用來確定表項排序時是基於表項文本的升序方式;
LVS_SORTDESCENDING 用來確定表項排序時是基於表項文本的降序方式;
2 、列表控制的屬性類
列表控制的屬性類包括取得列表控制的背景色GetBkColor 、設置列表控制的背景色SetBkColor 、取得列表控制的圖像列表 GetImageList 、設置列表控制的圖像列表SetImageList 、取得列表項數目GetItemCount 、取得列表控制的屬性 GetItem 、取得與表項相關的數據GetItemData 、設置表項的屬性SetItem 、設置與表項相關的數值SetItemData 、取得相關聯 的下一個表項GetNextItem 、設置列表控制的文本顏色SetTextColor 、取得列表控制的文本背景顏色GetTextBkColor 、設置 表項的最大數目SetItemCount 和取得被選中表項的數目GetSelectedCount 等。
3 、列表控制的操作方法
列表控制的操作方法包括插入一個新的表項InsertItem 、刪除一個表項DeleteItem 、排序表項SortItems 、測試列表的位 置HitTest 、重繪表項RedrawItems 、插入一個表列InsertColumn 、刪除一個表列DeleteColumn 、編輯一個表項文本 EditLabel 和重繪一個表項DrawItem 等。
(三)列表控制的數據結構
列表控制中包含兩個非常重要的數據結構LV_ITEM 和LV_COLUMN 。LV_ITEM 用於定義列表控制的一個表項,LV_COLUMN 用於定義列表控制的一個表列,其定義格式分別爲:
typedef struct _LV_ITEM { UINT mask; // 結構成員屏蔽位 int iItem; // 表項索引號 int iSubItem; // 子表項索引號 UINT state; // 表項狀態 UINT stateMask; // 狀態有效性屏蔽位 LPTSTR pszText; // 表項名文本 int cchTextMax; // 表項名最大長度 int iImage; // 表項圖標的索引號 LPARAM lParam; // 與表項相關的32 位數 } LV_ITEM; typedef struct _LV_COLUMN { UINT mask; // 結構成員有效性屏蔽位 int fmt; // 表列對齊方式 int cx; // 表列的象素寬度 LPTSTR pszText; // 表列的表頭名 int cchTextMax; // 表列名的文本長度 int iSubItem; // 與表列關聯的子表項索引號 } LV_COLUMN; |
其中fmt 可以取如下值:
LVCFMT_CENTER 表列居中對齊
LVCFMT_LEFT 表列左對齊
(四)列表控制的應用技巧示例
本文給出具體實例演示列表控制及前面的表頭控制和圖像列表的應用技巧。步驟如下:
1 、通過“FILE->NEW->PROJECTS->MFC AppWizard(EXE)” 建立名爲VCLIST 的工程,在建立過程中選擇基於對話框(Dialog based )的應用;將對話框中的默認控件刪除,並將所有對話框屬性中的Language 域設置爲Chinese(P.R.C.), 以使應用程序支持中 文;
建立兩個圖標IDI_GJ
和IDI_XS
,用來表示圖標的選中和非選中狀態,對於每個圖標都應建立32X32
和16X16
兩種大小,以保證程序的需要;
3
、在對話框窗口中設計組合框(Group Box
),
組合框中設置四個無線按鈕(Radio
)“
大圖標|小圖標|列表|資料”
,同時設置排序、刪除和關閉三個控制按鈕(Button
),並在對話框 中設置大小合適的列表控制(List Ctrl
),其對應標識分別如下:
--------------------------------------------------------------------------------
控制名稱 標題名稱 標識符號
--------------------------------------------------------------------------------
列表控制 IDC_LISTCTRL
組合框 方式 IDC_STATIC
無線按鈕 大圖標 IDC_STDICON
小圖標 IDC_SMLICON
列 表 IDC_LIST
資 料 IDC_REPORT
按鈕 排 序 IDC_SORT
刪 除 IDC_DEL
關 閉 IDOK
--------------------------------------------------------------------------------
4 、在設置無線按鈕時,需要注意的是隻有大圖標的Group 屬性爲選中狀態,而其它無線按鈕的狀態均爲默認值。
5 、選中列表控制控件,選擇“VIEW->ClassWizard->Memory Variables” ,並利用IDC_ LISTCTRL 引入成員變量,其變量類型爲:
變量名 種類 變量類型
m_ListCtrl Control ClistCtrl
同時利用“MESSAGES MAP” 爲各無線按鈕和命令按鈕增加控制功能。
6 、然後在包含文件和代碼文件中分別加入如下代碼:
(1 )在VCLISTDlg.h 中增加數據結構和定義
typedef struct tagSPS { // 定義結構 char szPm[10]; // 品名 int Lx; //0-GJ 1-XS char szSl[10]; // 數量 char szDj[10]; // 單價 char szJe[10]; // 金額 } SPS; int CALLBACK CompareFunc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort); |
(2 )在VCLISTDlg.CPP 中的起始處增加初始化數據和程序定義
// 在文件開始處增加數據結構初始化 SPS Sps[]={// 信息 {" 紅梅",0,"1000","30","30000"}, {" 黃梅",0,"1000","29","29000"}, {" 綠梅",0,"1000","28","28000"}, {" 青梅",0,"1000","27","27000"}, {" 白梅",0,"1000","31","31000"}, {" 紅梅",1,"1000","30","30000"}, {" 黃梅",1,"1000","29","29000"}, {" 綠梅",1,"1000","28","28000"}, {" 青梅",1,"1000","27","27000"}, {" 白梅",1,"1000","31","31000"}}; CImageList Cil1,Cil2;// 大小圖像列表 |
(3 )在程序初始化處增加表頭、圖像和列表控制建立代碼
BOOL CVCLISTDlg::OnInitDialog() {CDialog::OnInitDialog(); //......// 其它代碼 // TODO: Add extra initialization here 此處增加代碼 LV_ITEM lvitem; LV_COLUMN lvcol; int i,iPos,iItemNum; CVCLISTApp *pApp=(CVCLISTApp *)AfxGetApp();// 創建圖象列表 Cil1.Create(32,32,TRUE,2,2); Cil1.Add(pApp->LoadIcon(IDI_GJ)); Cil1.Add(pApp->LoadIcon(IDI_XS)); Cil2.Create(16,16,TRUE,2,2); Cil2.Add(pApp->LoadIcon(IDI_GJ)); Cil2.Add(pApp->LoadIcon(IDI_XS));// 設置圖象列表 m_ListCtrl.SetImageList(&Cil1,LVSIL_NORMAL); m_ListCtrl.SetImageList(&Cil2,LVSIL_SMALL);// 向列表控制中添加表列 lvcol.mask=LVCF_FMT|LVCF_SUBITEM|LVCF_TEXT|LVCF_WIDTH; lvcol.fmt=LVCFMT_CENTER;// 居中 i=0; lvcol.pszText=" 品 名"; lvcol.iSubItem=i; lvcol.cx=70; m_ListCtrl.InsertColumn(i++,&lvcol); lvcol.pszText=" 數 量"; lvcol.iSubItem=i; lvcol.cx=70; m_ListCtrl.InsertColumn(i++,&lvcol); lvcol.pszText=" 單 價"; lvcol.iSubItem=i; lvcol.cx=70; m_ListCtrl.InsertColumn(i++,&lvcol); lvcol.pszText=" 金 額"; lvcol.iSubItem=i; lvcol.cx=70; m_ListCtrl.InsertColumn(i++,&lvcol); // 向列表控制中添加表項 iItemNum=sizeof(Sps)/sizeof(SPS); for(i=0;i<iItemNum;i++){ lvitem.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; lvitem.iItem=i; lvitem.iSubItem=0; lvitem.pszText=Sps[i].szPm; lvitem.iImage=Sps[i].Lx; lvitem.lParam=i; iPos=m_ListCtrl.InsertItem(&lvitem);// 返回表項插入後的索引號 lvitem.mask=LVIF_TEXT; lvitem.iItem=iPos; lvitem.iSubItem=1; lvitem.pszText=Sps[i].szSl; m_ListCtrl.SetItem(&lvitem); lvitem.iSubItem=2; lvitem.pszText=Sps[i].szDj; m_ListCtrl.SetItem(&lvitem); lvitem.iSubItem=3; lvitem.pszText=Sps[i].szJe; m_ListCtrl.SetItem(&lvitem); } CheckRadioButton(IDC_STDICON,IDC_REPORT,IDC_STDICON); return TRUE; // return TRUE unless you set the focus to a control } |
(4 )完善列表顯示方式代碼
在利用Classwizard 類嚮導創建各功能按鈕顯示功能函數之後,必須依次完善這些功能函數的代碼,這些功能函數如下:
void CVCLISTDlg::OnStdicon()// 設置大圖標顯示方式 { // TODO: Add your control notification handler code here LONG lStyle; lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);// 獲取當前窗口類型 lStyle&=~LVS_TYPEMASK; // 清除顯示方式位 lStyle|=LVS_ICON; // 設置顯示方式 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);// 設置窗口類型 } void CVCLISTDlg::OnSmlicon() // 設置小圖標顯示方式 { // TODO: Add your control notification handler code here LONG lStyle; lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);// 獲取當前窗口類型 lStyle&=~LVS_TYPEMASK; // 清除顯示方式位 lStyle|=LVS_SMALLICON; // 設置顯示方式 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);// 設置窗口類型 } void CVCLISTDlg::OnList() // 設置列表顯示方式 { // TODO: Add your control notification handler code here LONG lStyle; lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);// 獲取當前窗口類型 lStyle&=~LVS_TYPEMASK; // 清除顯示方式位 lStyle|=LVS_LIST; // 設置顯示方式 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);// 設置窗口類型 } void CVCLISTDlg::OnReport() // 詳細資料顯示方式 { // TODO: Add your control notification handler code here LONG lStyle; lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);// 獲取當前窗口類型 lStyle&=~LVS_TYPEMASK; // 清除顯示方式位 lStyle|=LVS_REPORT; // 設置顯示方式 SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);// 設置窗口類型 } |
(5 )刪除功能的實現
要實現刪除功能,必須取得選中表項的數和表項總數,並且需要從後向前進行依次刪除,其原因是每個表項被刪除後,其後各表項的索引號均會發生遞減變化,如果採取從前向後刪除的方法,就會造成無法正常刪除選中的表項,其功能代碼如下:
void CVCLISTDlg::OnDel() // 刪除按鈕功能 { // TODO: Add your control notification handler code here int i,iState; int nItemSelected=m_ListCtrl.GetSelectedCount();// 所選表項數 int nItemCount=m_ListCtrl.GetItemCount();// 表項總數 if(nItemSelected<1) return; for(i=nItemCount-1;i>=0;i--){ iState=m_ListCtrl.GetItemState(i,LVIS_SELECTED); if(iState!=0) m_ListCtrl.DeleteItem(i); } } |
(6 )排序功能的實現
列表控制有一個特殊的功能,當以詳細資料方式顯示時,列表頂部的表頭可以當作按鈕來使用,這可以通過列表控制創建時的風格來控制。當鼠標 點 擊列表頭名稱時,列表控制就會向其父窗口發送一個LNV_COLUMNCLICK 消息,利用類導向中列表控制IDC_LISTCTRL 對應的 LNV_COLUMNCLICK 消息加入相應處理函數,就可將表列按照特定順序進行排列。其函數使用方法見程序,其中iSort 爲排序的表列索引號, (PFNLVCOMPARE)CompareFunc 爲進行具體排序的回調函數,也就是說,通過鼠標點擊表頭實現的排序過程是由第三方開發的專用排序函數 來實現的,排序函數只是實現表項的具體比較操作,而整個排序過程是由SortItemS 屬性通過不斷調用這個函數來實現的。正常的排序過程是升序方式,通 過調換排序函數中的參數值,就可實現降序排列,即將PARAM1 與PARAM2 調換位置。這個回調函數的前兩個參數爲表列中表項的索引號,第三個參數爲排 序的表列索引號。
void CVCLISTDlg::OnColumnclickListctrl(NMHDR* pNMHDR, LRESULT* pResult) { // 鼠標左鍵單擊表頭處理函數 NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; // TODO: Add your control notification handler code here static int iSorted=-1;// 排列序號 if (pNMListView->iSubItem==iSorted) return; iSorted=pNMListView->iSubItem; m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,iSorted); *pResult = 0; } // 排序時比較表項的回調函數 int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,LPARAM lParamSort) { char *text1,*text2; switch (lParamSort){ case 0L:text1=Sps[lParam1].szPm; text2=Sps[lParam2].szPm;break; case 1L:text1=Sps[lParam1].szSl; text2=Sps[lParam2].szSl;break; case 2L:text1=Sps[lParam1].szDj; text2=Sps[lParam2].szDj;break; case 3L:text1=Sps[lParam1].szJe; text2=Sps[lParam2].szJe;break; } return (strcmp(text1,text2));// 結果爲>0 =0 <0 } |
同樣,也可以通過專用按鈕來實現排序功能,如本文的排序按鈕對應的功能代碼如下:
void CVCLISTDlg::OnSort() { // TODO: Add your control notification handler code here m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);} |
7 、列表視的演練技巧
在使用列表視時,其方法與列表控制基本相同,只不過列表視是在窗口中來實現的而列表控制是在對話框中實現,列表視的各種功能是通過菜單來實現的 而列表控制是通過按鈕等方式來實現的,列表控制需要在對話框中創建列表控制控件而列表視直接佔據整個窗口,在設計過程中只要將按鈕和列表控制設計過程變爲 菜單設計,並注意在功能增加是在類嚮導中是通過菜單命令來操作,同時在每個功能函數前面增加取得列表視引用的命令( CListCtrl& ListCtrl = GetListCtrl() ),而其餘數據結構和代碼均不需要修改,實現起來比較容易。