MFC CListCtrl 列表框排序 单击列头排序(完整源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客

        这是一个通用CListCtrl排序的源代码。

  • 对列表框CListCtrl进行排序,使用列表框的自定义排序功能SortItems
  • 支持任意多个排序列和多种排序规则
  • 单击列头排序可在控件通知HDN_ITEMCLICK事件中调用,示例代码就在头文件里

目录

一、关键技术

二、完整源码

三、代码详解

3.1 排序类型

3.2 排序的列

3.3 排序入口

3.4 回调函数

3.5 使用


一、关键技术

        CListCtrl::SortItems(回调函数,回调函数参数):

BOOL SortItems(
    PFNLVCOMPARE pfnCompare,
    DWORD_PTR dwData);

        该函数通过回调函数比较两个列,参数为(列索引1,列索引2,回调函数参数)。

        比较函数为:

int CALLBACK CompareFunc(LPARAM lParam1,
    LPARAM lParam2,
    LPARAM lParamSort);

        返回值规则为负值表示第一个排前面,正值表示第二个排前面,相当于用第一个减第二个。

        执行SortItems的时候会不断调用CompareFunc来确定排序关系,参数为要比较的两个行的索引以及SortItems的dwData,dwData我们可以设置为一个指针,从而包含需要的任何数据。 

        大致的执行流程是这样的:

SortItems(比较函数,参数)
------比较函数(0,1,参数)
------比较函数(1,2,参数)
------不断调用比较函数
完成

        本代码用回调函数参数传递排序规则,还将itemdata藏在一个临时列里面(动机是什么我忘了,如果没使用itemdata可以删掉相关代码,如果发现有什么用可以告诉我。感觉当初我应该不是傻了,应该是有理由的)。

        这个代码最早写于2000年,现在这个版本是可以用在VS2017的。

二、完整源码

头文件:

//SortList.h

#pragma once

//通用CListCtrl排序
//这个文件最早写于2000年
//对列表框CListCtrl进行排序,使用列表框的自定义排序功能SortItems
//支持任意多个排序列和多种排序规则
//单击列头排序可在控件通知HDN_ITEMCLICK事件中调用
//示例:
/*
void CTestDlg::OnHdnItemclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	*pResult = 0;

	static map<int, bool > map_isInc;//记录上次的排序状态
	map_isInc[phdr->iItem] = !map_isInc[phdr->iItem];//同一列每次取反

	CList<CompareFunc_Col> sortparam;
	CompareFunc_Col col;
	col.idCol = phdr->iItem;
	col.isInc = map_isInc[phdr->iItem];
	col.sorttype = enum_SortType::sorttype_str;
	sortparam.AddTail(col);
	SortList(&m_List1, &sortparam);
}
*/


//排序类型,数值还是字符串
enum enum_SortType
{
	sorttype_str,
	sorttype_istr,
	sorttype_trimstr,
	sorttype_itrimstr,
	sorttype_rtrimstr,
	sorttype_irtrimstr,
	sorttype_long,
	sorttype_double,
};

//排序的一个列
class CompareFunc_Col
{
public:
	int idCol = 0;
	int sorttype = sorttype_str;
	BOOL isInc = true;
};

//排序调用此功能即可,参数为一个或多个排序的列
void SortList(CListCtrl* pl, CList<CompareFunc_Col> * pSortCols);

//取得列索引,-1表示没找到(辅助函数,排序功能并不需要这个函数)
int GetColIndex(CListCtrl * pl, _TCHAR* Col);

实现文件:

//SortList.cpp

#include "pch.h" //预编译头文件,是否需要取决于项目设置
#include "SortList.h"

int GetColIndex(CListCtrl* pl, _TCHAR* Col)
{
	LVCOLUMN lvcol;
	_TCHAR str[256];
	int ii;

	lvcol.mask = LVCF_TEXT;
	lvcol.pszText = str;
	lvcol.cchTextMax = 256;
	ii = 0;
	while (pl->GetColumn(ii, &lvcol))
	{
		if (NULL != Col && 0 == _tcscmp(Col, str))
			return ii;
		ii++;
	}
	return -1;
}
int CompareStr(const _TCHAR* str1, const _TCHAR* str2, int type)
{
	CString cstr, cstr2;
	int ret;
	double d;

	cstr = str1;
	cstr2 = str2;
	switch (type)
	{
	case sorttype_str:
		ret = _tcscmp(cstr, cstr2);
		break;
	case sorttype_istr:
		ret = _tcsicmp(cstr, cstr2);
		break;
	case sorttype_trimstr:
		ret = _tcscmp(cstr.Trim(), cstr2.Trim());
		break;
	case sorttype_itrimstr:
		ret = _tcsicmp(cstr.Trim(), cstr2.Trim());
		break;
	case sorttype_rtrimstr:
		ret = _tcscmp(cstr.TrimRight(), cstr2.TrimRight());
		break;
	case sorttype_irtrimstr:
		ret = _tcsicmp(cstr.TrimRight(), cstr2.TrimRight());
		break;
	case sorttype_long:
		ret = _ttol(cstr) - _ttol(cstr2);
		break;
	case sorttype_double:
		d = _ttof(cstr) - _ttof(cstr2);
		if (d > 0)ret = 1;
		else if (0 == d)ret = 0;
		else ret = -1;
		break;
	default:
		ret = 0;
		break;
	}

	return ret;
}

//用于排序的回调函数的参数
struct CompareFunc_lParamSort
{
	CListCtrl* pl;
	CList<CompareFunc_Col>* pSortCols;
};

//用于排序的回调函数,lParamSort为CompareFunc_lParamSort
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CompareFunc_lParamSort* pparam;
	CompareFunc_Col* pcol;
	CString cstr, cstr2;
	POSITION pos;
	int ret;
	int ci;

	pparam = (CompareFunc_lParamSort*)lParamSort;

	ci = 0;
	pos = pparam->pSortCols->GetHeadPosition();
	while (NULL != pos)
	{
		pcol = &pparam->pSortCols->GetNext(pos);
		cstr = pparam->pl->GetItemText((int)lParam1, pcol->idCol);
		cstr2 = pparam->pl->GetItemText((int)lParam2, pcol->idCol);
		ret = CompareStr(cstr, cstr2, pcol->sorttype);
		if (0 != ret)
		{
			if (pcol->isInc)
			{
				return ret;
			}
			else
			{
				return -ret;
			}
		}
	}
	return 0;
}

void SortList(CListCtrl* pl, CList<CompareFunc_Col>* pSortCols)
{
	_TCHAR str[256];
	int ii, ci;

	//保存原来的ItemData到新列
	ci = pl->InsertColumn(pl->GetHeaderCtrl()->GetItemCount(), TEXT("_temp"));
	ii = pl->GetItemCount();
	while (ii--)
	{
		_i64tot_s(pl->GetItemData(ii), str, 256, 10);
		pl->SetItemText(ii, ci, str);
	}
	//设置IetmData
	ii = pl->GetItemCount();
	while (ii--)pl->SetItemData(ii, ii);

	//排序
	CompareFunc_lParamSort CompareParam;
	CompareParam.pl = pl;
	CompareParam.pSortCols = pSortCols;

	pl->SortItems(CompareFunc, (DWORD_PTR)&CompareParam);

	//恢复ItemData
	ii = pl->GetItemCount();
	while (ii--)
	{
		pl->SetItemData(ii, _ttoi64(pl->GetItemText(ii, ci)));
	}
	//删除临时列
	pl->DeleteColumn(ci);
}

三、代码详解

3.1 排序类型

//排序类型,数值还是字符串
enum enum_SortType
{
	sorttype_str,
	sorttype_istr,
	sorttype_trimstr,
	sorttype_itrimstr,
	sorttype_rtrimstr,
	sorttype_irtrimstr,
	sorttype_long,
	sorttype_double,
};

        这是我自己定义的排序类型,包括整数、双精度和各种字符串方式,i表示忽略大小写,trim表示忽略前后空白字符,rtrim表示只忽略右边的空白字符。你也可以增加自己的方式,甚至通过参数直接传递比较函数。

3.2 排序的列

//排序的一个列
class CompareFunc_Col
{
public:
	int idCol = 0;
	int sorttype = sorttype_str;
	BOOL isInc = true;
};

        idCol 列的索引

        sorttype 前面介绍的排序类型

        isInc 是否是增量排序

3.3 排序入口

//排序调用此功能即可,参数为一个或多个排序的列
void SortList(CListCtrl* pl, CList<CompareFunc_Col> * pSortCols);

        参数是要排序的CListCtrl和要排序的列(因为支持多个,所以参数是CList,当初我还不懂STL,放现在肯定用vector了)。

        排序入口做了两件事:

  • 保存ItemData到临时列,排序完成后再恢复(有点怀疑是因为排序的时候ItemData混乱了)
  • 设置参数调用CListCtrl的SortItems排序

3.4 回调函数

        回调函数的第三个参数用来传递外部数据:

//用于排序的回调函数的参数
struct CompareFunc_lParamSort
{
	CListCtrl* pl;
	CList<CompareFunc_Col>* pSortCols;
};

         看起来按照CListCtrl的设计,告诉你行的索引你就知道全部了??由于回调函数的参数只包含两个行的索引,所以我们必须把一切都塞在第三个参数里面,所以必须使用一个结构,把CListCtrl指针和排序规则放进去。

        回调函数里面则根据规则获取数据比较。

3.5 使用

//单击列头排序可在控件通知HDN_ITEMCLICK事件中调用
//示例:
/*
void CTestDlg::OnHdnItemclickList1(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMHEADER phdr = reinterpret_cast<LPNMHEADER>(pNMHDR);
	// TODO: 在此添加控件通知处理程序代码
	*pResult = 0;

	static map<int, bool > map_isInc;//记录上次的排序状态
	map_isInc[phdr->iItem] = !map_isInc[phdr->iItem];//同一列每次取反

	CList<CompareFunc_Col> sortparam;
	CompareFunc_Col col;
	col.idCol = phdr->iItem;
	col.isInc = map_isInc[phdr->iItem];
	col.sorttype = enum_SortType::sorttype_str;
	sortparam.AddTail(col);
	SortList(&m_List1, &sortparam);
}
*/

         上面的代码在列表框控件的HDN_ITEMCLICK事件通知里处理,只使用单击的列排序,连续单击多个列头可实现复合排序的效果,重复单击同一个列头则改变递增、递减。

        这个代码我用了很多年。

(这里是结束)

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值