(四)列表控制的應(yīng)用技巧示例
本文給出具體實(shí)例演示列表控制及前面的表頭控制和圖像列表的應(yīng)用技巧。步驟如下:
1、通過(guò)“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名為VCLIST的工程,在建立過(guò)程中選擇基于對(duì)話框(Dialog based)的應(yīng)用;將對(duì)話框中的默認(rèn)控件刪除,并將所有對(duì)話框?qū)傩灾械腖anguage域設(shè)置為Chinese(P.R.C.),以使應(yīng)用程序支持中文;
建立兩個(gè)圖標(biāo)IDI_GJ和IDI_XS,用來(lái)表示圖標(biāo)的選中和非選中狀態(tài),對(duì)于每個(gè)圖標(biāo)都應(yīng)建立32X32和16X16兩種大小,以保證程序的需要;
3、在對(duì)話框窗口中設(shè)計(jì)組合框(Group Box),組合框中設(shè)置四個(gè)無(wú)線按鈕(Radio)“大圖標(biāo)|小圖標(biāo)|列表|資料”,同時(shí)設(shè)置排序、刪除和關(guān)閉三個(gè)控制按鈕(Button),并在對(duì)話框中設(shè)置大小合適的列表控制(List Ctrl),其對(duì)應(yīng)標(biāo)識(shí)分別如下:
--------------------------------------------------------------------------------
控制名稱 標(biāo)題名稱 標(biāo)識(shí)符號(hào)
--------------------------------------------------------------------------------
列表控制 IDC_LISTCTRL
組合框 方式 IDC_STATIC
無(wú)線按鈕 大圖標(biāo) IDC_STDICON
小圖標(biāo) IDC_SMLICON
列 表 IDC_LIST
資 料 IDC_REPORT
按鈕 排 序 IDC_SORT
刪 除 IDC_DEL
關(guān) 閉 IDOK
--------------------------------------------------------------------------------
4、在設(shè)置無(wú)線按鈕時(shí),需要注意的是只有大圖標(biāo)的Group屬性為選中狀態(tài),而其它無(wú)線按鈕的狀態(tài)均為默認(rèn)值。
5、選中列表控制控件,選擇“VIEW->ClassWizard->Memory Variables”,并利用IDC_ LISTCTRL引入成員變量,其變量類型為:
變量名 種類 變量類型
m_ListCtrl Control ClistCtrl
同時(shí)利用“MESSAGES MAP”為各無(wú)線按鈕和命令按鈕增加控制功能。
6、然后在包含文件和代碼文件中分別加入如下代碼:
(1)在VCLISTDlg.h中增加數(shù)據(jù)結(jié)構(gòu)和定義
(2)在VCLISTDlg.CPP中的起始處增加初始化數(shù)據(jù)和程序定義
//在文件開始處增加數(shù)據(jù)結(jié)構(gòu)初始化

 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();//創(chuàng)建圖象列表

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));//設(shè)置圖象列表

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="數(shù) 量";

lvcol.iSubItem=i;

lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol);

lvcol.pszText="單 價(jià)";

lvcol.iSubItem=i;

lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol);

lvcol.pszText="金 額";

lvcol.iSubItem=i;

lvcol.cx=70;

m_ListCtrl.InsertColumn(i++,&lvcol);

//向列表控制中添加表項(xiàng)

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);//返回表項(xiàng)插入后的索引號(hào)

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類向?qū)?chuàng)建各功能按鈕顯示功能函數(shù)之后,必須依次完善這些功能函數(shù)的代碼,這些功能函數(shù)如下:


void CVCLISTDlg::OnStdicon()//設(shè)置大圖標(biāo)顯示方式



{ // TODO: Add your control notification handler code here

LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當(dāng)前窗口類型

lStyle&=~LVS_TYPEMASK; //清除顯示方式位

lStyle|=LVS_ICON; //設(shè)置顯示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設(shè)置窗口類型

}

void CVCLISTDlg::OnSmlicon() //設(shè)置小圖標(biāo)顯示方式



{ // TODO: Add your control notification handler code here

LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當(dāng)前窗口類型

lStyle&=~LVS_TYPEMASK; //清除顯示方式位

lStyle|=LVS_SMALLICON; //設(shè)置顯示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設(shè)置窗口類型

}

void CVCLISTDlg::OnList() //設(shè)置列表顯示方式



{ // TODO: Add your control notification handler code here

LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當(dāng)前窗口類型

lStyle&=~LVS_TYPEMASK; //清除顯示方式位

lStyle|=LVS_LIST; //設(shè)置顯示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設(shè)置窗口類型

}

void CVCLISTDlg::OnReport() //詳細(xì)資料顯示方式



{ // TODO: Add your control notification handler code here

LONG lStyle;

lStyle=GetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE);//獲取當(dāng)前窗口類型

lStyle&=~LVS_TYPEMASK; //清除顯示方式位

lStyle|=LVS_REPORT; //設(shè)置顯示方式

SetWindowLong(m_ListCtrl.m_hWnd,GWL_STYLE,lStyle);//設(shè)置窗口類型

}


(5)刪除功能的實(shí)現(xiàn)
要實(shí)現(xiàn)刪除功能,必須取得選中表項(xiàng)的數(shù)和表項(xiàng)總數(shù),并且需要從后向前進(jìn)行依次刪除,其原因是每個(gè)表項(xiàng)被刪除后,其后各表項(xiàng)的索引號(hào)均會(huì)發(fā)生遞減變化,如果采取從前向后刪除的方法,就會(huì)造成無(wú)法正常刪除選中的表項(xiàng),其功能代碼如下:


void CVCLISTDlg::OnDel() //刪除按鈕功能



{ // TODO: Add your control notification handler code here

int i,iState;

int nItemSelected=m_ListCtrl.GetSelectedCount();//所選表項(xiàng)數(shù)

int nItemCount=m_ListCtrl.GetItemCount();//表項(xiàng)總數(shù)

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)排序功能的實(shí)現(xiàn)
列表控制有一個(gè)特殊的功能,當(dāng)以詳細(xì)資料方式顯示時(shí),列表頂部的表頭可以當(dāng)作按鈕來(lái)使用,這可以通過(guò)列表控制創(chuàng)建時(shí)的風(fēng)格來(lái)控制。當(dāng)鼠標(biāo)點(diǎn)擊列表頭名稱時(shí),列表控制就會(huì)向其父窗口發(fā)送一個(gè)LNV_COLUMNCLICK消息,利用類導(dǎo)向中列表控制IDC_LISTCTRL對(duì)應(yīng)的LNV_COLUMNCLICK消息加入相應(yīng)處理函數(shù),就可將表列按照特定順序進(jìn)行排列。其函數(shù)使用方法見程序,其中iSort為排序的表列索引號(hào),(PFNLVCOMPARE)CompareFunc為進(jìn)行具體排序的回調(diào)函數(shù),也就是說(shuō),通過(guò)鼠標(biāo)點(diǎn)擊表頭實(shí)現(xiàn)的排序過(guò)程是由第三方開發(fā)的專用排序函數(shù)來(lái)實(shí)現(xiàn)的,排序函數(shù)只是實(shí)現(xiàn)表項(xiàng)的具體比較操作,而整個(gè)排序過(guò)程是由SortItemS屬性通過(guò)不斷調(diào)用這個(gè)函數(shù)來(lái)實(shí)現(xiàn)的。正常的排序過(guò)程是升序方式,通過(guò)調(diào)換排序函數(shù)中的參數(shù)值,就可實(shí)現(xiàn)降序排列,即將PARAM1與PARAM2調(diào)換位置。這個(gè)回調(diào)函數(shù)的前兩個(gè)參數(shù)為表列中表項(xiàng)的索引號(hào),第三個(gè)參數(shù)為排序的表列索引號(hào)。


void CVCLISTDlg::OnColumnclickListctrl(NMHDR* pNMHDR, LRESULT* pResult)



{ //鼠標(biāo)左鍵單擊表頭處理函數(shù)

NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

// TODO: Add your control notification handler code here

static int iSorted=-1;//排列序號(hào)

if (pNMListView->iSubItem==iSorted) return;

iSorted=pNMListView->iSubItem;

m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,iSorted);

*pResult = 0;

}

//排序時(shí)比較表項(xiàng)的回調(diào)函數(shù)

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));//結(jié)果為>0 =0 <0

}


同樣,也可以通過(guò)專用按鈕來(lái)實(shí)現(xiàn)排序功能,如本文的排序按鈕對(duì)應(yīng)的功能代碼如下:


void CVCLISTDlg::OnSort()



{ // TODO: Add your control notification handler code here

m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);}


7、列表視的演練技巧
在使用列表視時(shí),其方法與列表控制基本相同,只不過(guò)列表視是在窗口中來(lái)實(shí)現(xiàn)的而列表控制是在對(duì)話框中實(shí)現(xiàn),列表視的各種功能是通過(guò)菜單來(lái)實(shí)現(xiàn)的而列表控制是通過(guò)按鈕等方式來(lái)實(shí)現(xiàn)的,列表控制需要在對(duì)話框中創(chuàng)建列表控制控件而列表視直接占據(jù)整個(gè)窗口,在設(shè)計(jì)過(guò)程中只要將按鈕和列表控制設(shè)計(jì)過(guò)程變?yōu)椴藛卧O(shè)計(jì),并注意在功能增加是在類向?qū)е惺峭ㄟ^(guò)菜單命令來(lái)操作,同時(shí)在每個(gè)功能函數(shù)前面增加取得列表視引用的命令( CListCtrl& ListCtrl = GetListCtrl()),而其余數(shù)據(jù)結(jié)構(gòu)和代碼均不需要修改,實(shí)現(xiàn)起來(lái)比較容易。