内容摘要
列表排序功能在很多报表功能的软件中应用很多,本文只介绍如何快速的实现简单的列表排序功能。后续又添加了如何在列表头添加排序图标。
列表排序
实现列表排序功能的方法很多,网上搜索了一下,每个人的理解不同,实现方法也各不相同。大部分人的实现是基于CListCtrl控件提供的SortItems方法,本文中也将介绍该方法实现。
【题外话】首先,想谈一个C++程序员的弊端,C++程序员非常喜欢将功能封装到类中,并且所有的方法和变量都要做成类成员。其实,仔细研究一下类,实际上也只是一种数据类型。就我遇到的情况,有时候需要在一个类中使用的线程,则非常希望把线程函数写到类中,貌似这样就能保证线程的安全,并且保证这个线程和这个类之间的某种必然的关系。或者有时候的某个变量,分明是一个全局的变量,可以供多个类或者是模块使用,但大部分程序员不自觉的就将该变量做成类变量,然后使用一堆的机制,保证各个模块共享该变量。
最近自己意识到这个问题,所以想重点提一下,C++程序员确实被类的封装给禁锢的太深了。
闲话少说,开始今天的话题。
- 响应列表控件的LVN_COLUMNCLICK消息,在该消息响应函数中调用SortItems函数,实现文件列表的排序。
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
static int nCurSortCol = -1; // 保存当前的排序列表
// 一开始表示为-1,标识尚未按任何列排序
// 若当前排序列与点击列相同,则更改增、减的排序方式。
// 若点击列与当前排序列不同的列,则改变排序列,并将排序方式改为增序
if (nCurSortCol == pNMListView->iSubItem)
{
if (nSortOrder == 1)
{
nSortOrder = -1;
}else
{
nSortOrder = 1;
}
}else
{
nCurSortCol = pNMListView->iSubItem;
nSortOrder = 1;
}
Item mItem
mItem.mList = &m_FileList;
mItem.Isub = pNMListView->iSubItem;
for (int i = 0; i < m_FileList.GetItemCount(); i++)
{
m_FileList.SetItemData(i,i);
}
m_FileList.SortItems(CompareFunc, (LPARAM)&mItem);
SetSortArrow(nCurSortCol,nSortOrder < 0 ? true : false);
int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
// 得到排序方式
if (lParam1 == 0 || lParam2 == 0) // 禁止第一行排序
{
return 0;
}
Item *pItem = (Item*)lParamSort;
ASSERT(pItem);
// 得到两个列的排序信息
CString str1 = pItem->mList->GetItemText(lParam1,pItem->Isub);
CString str2 = pItem->mList->GetItemText(lParam2,pItem->Isub);
switch(pItem->Isub)
{
default:
ASSERT(0);
return 0;
case 0:
{
if (nSortOrder == 1)
{
return str1.Compare(str2);
}else
{
return -str1.Compare(str2);
}
}
break;
case 1:
{
double dbVal1 = _wtof(str1.GetString());
double dbVal2 = _wtof(str2.GetString());
if (nSortOrder == 1)
{
return dbVal1 <= dbVal2 ? 1 : -1;
}else
{
return dbVal1 <= dbVal2 ? -1 : 1;
}
}
break;
}
return 0;
}
- 排序状态图标
// SetSortArrow ( int colIndex, bool ascending)
#if (_WIN32_WINNT >= 0x501)
for (int i = 0; i < m_FileList.GetHeaderCtrl()->GetItemCount(); ++i)
{
HDITEM hditem = {0};
hditem.mask = HDI_FORMAT;
VERIFY( m_FileList.GetHeaderCtrl()->GetItem( i, &hditem ) );
hditem.fmt &= ~(HDF_SORTDOWN|HDF_SORTUP);
if (i == colIndex)
{
hditem.fmt |= ascending ? HDF_SORTDOWN : HDF_SORTUP;
}
VERIFY( m_FileList.GetHeaderCtrl()->SetItem( i, &hditem) );
}
#endif
UINT bitmapID = m_Ascending ? IDB_DOWNARROW : IDB_UPARROW;
for(int i = 0; i < GetHeaderCtrl()->GetItemCount(); ++i)
{
HDITEM hditem = {0};
hditem.mask = HDI_BITMAP | HDI_FORMAT;
VERIFY( GetHeaderCtrl()->GetItem( i, &hditem ) );
if (hditem.fmt & HDF_BITMAP && hditem.fmt & HDF_BITMAP_ON_RIGHT)
{
if (hditem.hbm)
{
DeleteObject(hditem.hbm);
hditem.hbm = NULL;
}
hditem.fmt &= ~(HDF_BITMAP|HDF_BITMAP_ON_RIGHT);
VERIFY( CListCtrl::GetHeaderCtrl()->SetItem( i, &hditem ) );
}
if (i == colIndex)
{
hditem.fmt |= HDF_BITMAP|HDF_BITMAP_ON_RIGHT;
hditem.hbm = (HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(bitmapID), IMAGE_BITMAP, 0,0, LR_LOADMAP3DCOLORS);
VERIFY( hditem.hbm!=NULL );
VERIFY( CListCtrl::GetHeaderCtrl()->SetItem( i, &hditem ) );
}
}