在ListCtrl中进行排序问题

来源:互联网 发布:python 排序 编辑:程序博客网 时间:2024/05/29 19:13

列表控件(CListCtrl)的顶部有一排按钮,用户可以通过选择不同的列来对记录进行排序。但是 CListCtrl并没有自动排序的功能,我们需要自己添加一个用于排序的回调函数来比较两个数据的大小,此外还需要响应排序按钮被点击的消息。下面讲述一下具体的做法。

CListCtrl提供了用于排序的函数,函数原型为:BOOL CListCtrl::SortItems( PFNLVCOMPARE pfnCompare, DWORD 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{// Constructionpublic:CSortList();BOOL m_fAsc;//是否顺序排序int m_nSortedCol;//当前排序的列protected://{{AFX_MSG(CSortList)//}}AFX_MSG...};//父窗口中包含该CListCtrl派生类对象class CSort_in_list_ctrlDlg : public CDialog{// Constructionpublic: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 };CSortListm_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_MAPEND_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;elseiCompRes=(pInfo1->iAge < pInfo2->iAge)?-1:1;break;default:ASSERT(0);break;}//根据当前的排序方式进行调整if(pV->m_fAsc)return iCompRes;elsereturn iCompRes*-1;}
关于"在ListCtrl中进行排序问题"的补充
如果没有定义DEMO_DATA,如何直接用GetItemText得到文本进行排序。
这里的关键问题是在
1. 在InsertItem及SetItemText后,用BOOL CListCtrl::SetItemData( int nItem, DWORD dwData )来对每一项的ItemData进行设置。目的是:设置每项的ItemData为数据的索引,在排序函数中通过该ItemData来确定数据
2. 排序函数的参数LPARAM lParam1, LPARAM lParam2是进行比较的两项的ItemData。用int FindItem( LVFINDINFO* pFindInfo, int nStart = -1 ) const;得到ItemData对应Item。
3. 根据此Item,由Cstring GetItemText( int nItem, int nSubItem ) const;得到对应Cstring4. 用strcmp进行比较.
下面是排序函数的实现:
int CALLBACK ListCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CSortList* pV=(CSortList*)lParamSort;
CString strItem1, strItem2;
LVFINDINFO info;
int nIndex; info.flags = LVFI_PARAM;
//设置为LVFI_PARAM,后面的FindItem才能有效,详情参考MSDN的LVFINDINFO
info.lParam = lParam1;
//由item data(lParam1)得到对应的item text
if ( (nIndex=pV->FindItem(&info)) != -1)
strItem1 = pV->GetItemText(nIndex, pV->m_nSortedCol);
info.lParam = lParam2;
//由item data(lParam2)得到对应的item text
if ( (nIndex=pV->FindItem(&info)) != -1)
strItem2 = pV->GetItemText(nIndex, pV->m_nSortedCol);
int iCompRes = strcmp(strItem1, strItem2);
//根据当前的排序方式进行调整
if(pV->m_fAsc) return iCompRes; else return iCompRes*-1;
}