(一)列表控制的主要功能 列表控制和視(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()),而其餘資料結構和代碼均不需要修改,實現起來比較容易。 |