在ListCtrl中进行排序问题

列表控件(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
{
// 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;
}
 
关于"在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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值