在ListCtrl中進行排序 (轉)

worldblog發表於2007-12-09
在ListCtrl中進行排序 (轉)[@more@]//轉自MFC開發指南,/"> 

  在ListCtrl中進行排序
聞怡洋 wyy_cq@21cn. 
列表(CListCtrl)的頂部有一排按鈕,可以透過選擇不同的列來對記錄進行排序。但是 CListCtrl並沒有自動排序的功能,我們需要自己新增一個用於排序的回撥來比較兩個資料的大小,此外還需要響應排序按鈕被點選的訊息。下面講述一下具體的做法。

CListCtrl提供了用於排序的函式,函式原型為:BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, D dwData )。其中第一個引數為全域性排序函式的地址,第二個引數為使用者資料,你可以根據你的需要傳遞一個資料或是指標。該函式返回-1代表第一項排應在第二項前面,返回1代表第一項排應在第二項後面,返回0代表兩項相等。

用於排序的函式原形為:int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort),其中第三個引數為者傳遞的資料(即呼叫SortItems時的第二個引數dwData)。第一和第二個引數為用於比較的兩項的ItemData,你可以透過DWORD CListCtrl::GetItemData( int nItem )/BOOL CListCtrl::SetItemData( int nItem, DWORD dwData )來對每一項的ItemData進行存取。在新增項時選用特定的CListCtrl::InsertItem也可以設定該值。由於你在排序時只能透過該值來確定項的位置所以你應該比較明確的確定該值的含義。

最後一點,我們需要知道什麼時候需要排序,實現這點可以在父視窗中對LVN_COLUMNCLICK訊息進行處理來實現。

下面我們看一個例子,這個例子是一個派生類,並支援順序/倒序兩種方式排序。為了簡單我對全域性資料進行排序,而在實際應用中會有多組需要排序的資料,所以需要透過傳遞引數的方式來告訴派序函式需要對什麼資料進行排序。


//全域性資料
struct DEMO_DATA
{
char szName[20];
int iAge;
}strAllData[5]={{"王某",30},{"張某",40},{"武某",32},{"陳某",20},{"李某",36}};

//CListCtrl派生類定義
class CSortList : public CListCtrl
{
// Construction
public:
CSortList();
BOOL m_fAsc;//是否順序排序
int m_nSortedCol;//當前排序的列
protected:
//{{AFX_MSG(CSortList)
//}}AFX_MSG
...
};

//父視窗中包含該CListCtrl派生類
class CSort_in_list_ctrlDlg : public CDialog
{
// Construction
public:
CSort_in_list_ctrlDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
//{{AFX_DATA(CSort_in_list_ctrlDlg)
enum { IDD = IDD_SORT_IN_LIST_CTRL_DIALOG };
CSortList m_listTest;
//}}AFX_DATA
}

//在父視窗中定義LVN_COLUMNCLICK訊息對映
BEGIN_MESSAGE_MAP(CSort_in_list_ctrlDlg, CDialog)
//{{AFX_MSG_MAP(CSort_in_list_ctrlDlg)
ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnColumnclickList1)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

//初始化資料
BOOL CSort_in_list_ctrlDlg::OnInitDialog()
{
CDialog::OnInitDialog();

//初始化ListCtrl中資料列表
m_listTest.InsertColumn(0,"姓名");
m_listTest.InsertColumn(1,"年齡");
m_listTest.SetColumnWidth(0,80);
m_listTest.SetColumnWidth(1,80);
for(int i=0;i<5;i++)
{
m_listTest.InsertItem(i,strAllData[i].szName);
char szAge[10];
sprintf(szAge,"%d",strAllData[i].iAge);
m_listTest.SetItemText(i,1,szAge);
//設定每項的ItemData為陣列中資料的
//在排序函式中透過該ItemData來確定資料
m_listTest.SetItemData(i,i);
}
return TRUE;  // return TRUE  unless you set the focus to a control
}

//處理訊息
void CSort_in_list_ctrlDlg::OnColumnclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
//設定排序方式
if( pNMListView->iSubItem == m_listTest.m_nSortedCol )
m_listTest.m_fAsc = !m_listTest.m_fAsc;
else
{
m_listTest.m_fAsc = TRUE;
m_listTest.m_nSortedCol = pNMListView->iSubItem;
}
//呼叫排序函式
m_listTest.SortItems( ListCompare, (DWORD)&m_listTest );       
*pResult = 0;
}

//排序函式實現
int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
//透過傳遞的引數來得到CSortList物件指標,從而得到排序方式
CSortList* pV=(CSortList*)lParamSort;

//透過ItemData來確定資料
DEMO_DATA* pInfo1=strAllData+lParam1;
DEMO_DATA* pInfo2=strAllData+lParam2;
CString szComp1,szComp2;
int iCompRes;
switch(pV->m_nSortedCol)
{
case(0):
//以第一列為根據排序
szComp1=pInfo1->szName;
szComp2=pInfo2->szName;
iCompRes=szComp1.Compare(szComp2);
break;
case(1):
//以第二列為根據排序
if(pInfo1->iAge == pInfo2->iAge)
iCompRes = 0;
else
iCompRes=(pInfo1->iAge < pInfo2->iAge)?-1:1;
break;
default:
ASSERT(0);
break;
}
//根據當前的排序方式進行調整
if(pV->m_fAsc)
return iCompRes;
else
return iCompRes*-1;
}



本文示範程式碼




來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990596/,如需轉載,請註明出處,否則將追究法律責任。

相關文章