Sort list (numeric/text) using callback

This is a small class which permits to sort list controls. It works "substituting" every original item data with a small class (preserving the value), sort it using the standard (fast!) list control way, and then putting the things right back.

Step 1: implementing the class

The class will contain two subclasses to manage numeric and text column data. The numeric is provided to avoid the sorting of 11 before 2 as it would occur with string comparison; there are also 4 comparing routines (for ascending and descending text and integer columns).

The header file of the class should be like this:

class CSortClass
{
public:
	CSortClass(CListCtrl * _pWnd, const int _iCol, const bool _bIsNumeric);
	virtual ~CSortClass();
	
	int iCol;
	CListCtrl * pWnd;
	bool bIsNumeric;
	void Sort(const bool bAsc);
	
	static int CALLBACK CompareAsc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
	static int CALLBACK CompareDes(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
	
	static int CALLBACK CompareAscI(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
	static int CALLBACK CompareDesI(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
	
public:
	class CSortItem
	{
	public:
		virtual  ~CSortItem();
		CSortItem(const DWORD _dw, const CString &_txt);
		CString txt;
		DWORD dw;
	};
	class CSortItemInt
	{
	public:
		CSortItemInt(const DWORD _dw, const CString &_txt);
		int iInt ;
		DWORD dw;
	};
};

The CPP file will implement all the above:

/////////////////////////////////////////////////////////////////////////////
// CSortClass

CSortClass::CSortClass(CListCtrl * _pWnd, const int _iCol, const bool _bIsNumeric)
{
	iCol = _iCol;
	pWnd = _pWnd;
	bIsNumeric = _bIsNumeric;
	
	ASSERT(pWnd);
	int max = pWnd->GetItemCount();
	DWORD dw;
	CString txt;
	if (bIsNumeric)
	{
		for (int t = 0; t < max; t++)
		{
			dw = pWnd->GetItemData(t);
			txt = pWnd->GetItemText(t, iCol);
			pWnd->SetItemData(t, (DWORD) new CSortItemInt(dw, txt));
		}
	}
	else
	{
		for (int t = 0; t < max; t++)
		{
			dw = pWnd->GetItemData(t);
			txt = pWnd->GetItemText(t, iCol);
			pWnd->SetItemData(t, (DWORD) new CSortItem(dw, txt));
		}
	}
}

CSortClass::~CSortClass()
{
	ASSERT(pWnd);
	int max = pWnd->GetItemCount();
	if (bIsNumeric)
	{
		CSortItemInt * pItem;
		for (int t = 0; t < max; t++)
		{
			pItem = (CSortItemInt *) pWnd->GetItemData(t);
			ASSERT(pItem);
			pWnd->SetItemData(t, pItem->dw);
			delete pItem;
		}
	}
	else
	{
		CSortItem * pItem;
		for (int t = 0; t < max; t++)
		{
			pItem = (CSortItem *) pWnd->GetItemData(t);
			ASSERT(pItem);
			pWnd->SetItemData(t, pItem->dw);
			delete pItem;
		}
	}
}

void
CSortClass::Sort(const bool bAsc)
{
	if (bIsNumeric)
	{
		if (bAsc)
			pWnd->SortItems(CompareAscI, 0L);
		else
			pWnd->SortItems(CompareDesI, 0L);
	}
	else
	{
		if (bAsc)
			pWnd->SortItems(CompareAsc, 0L);
		else
			pWnd->SortItems(CompareDes, 0L);
	}
}

int CALLBACK CSortClass::CompareAsc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CSortItem * i1 = (CSortItem *) lParam1;
	CSortItem * i2 = (CSortItem *) lParam2;
	ASSERT(i1 && i2);
	return i1->txt.CompareNoCase(i2->txt);
}

int CALLBACK CSortClass::CompareDes(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CSortItem * i1 = (CSortItem *) lParam1;
	CSortItem * i2 = (CSortItem *) lParam2;
	ASSERT(i1 && i2);
	return i2->txt.CompareNoCase(i1->txt);
}

int CALLBACK CSortClass::CompareAscI(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CSortItemInt * i1 = (CSortItemInt *) lParam1;
	CSortItemInt * i2 = (CSortItemInt *) lParam2;
	ASSERT(i1 && i2);
	if (i1->iInt == i2->iInt) return 0;
	return i1->iInt > i2->iInt ? 1 : -1;
}

int CALLBACK CSortClass::CompareDesI(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CSortItemInt * i1 = (CSortItemInt *) lParam1;
	CSortItemInt * i2 = (CSortItemInt *) lParam2;
	ASSERT(i1 && i2);
	if (i1->iInt == i2->iInt) return 0;
	return i1->iInt < i2->iInt ? 1 : -1;
}

CSortClass::CSortItem::CSortItem(const DWORD _dw, const CString & _txt)
{
	dw = _dw;
	txt = _txt;
}

CSortClass::CSortItem::~CSortItem()
{
}

CSortClass::CSortItemInt::CSortItemInt(const DWORD _dw, const CString & _txt)
{
	iInt = atoi(_txt);
	dw = _dw;
}

Step 2: using it

The class is designed to be easy usable. In answer to a column click message in a list control, we can write something like this:
void CMyListCtrl::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	
	// bAscending will be use to order from lower to higher or higher to lower
	bool bAscending = true;
	
	CSortClass csc(this, pNMListView->iSubItem, bAscending);
	csc.Sort(bAsc);
	
	*pResult = 0;
}



Comments

  • Adding a double sort

    Posted by Legacy on 03/31/2003 12:00am

    Originally posted by: Bud Baker

    Thanks, Iuri.  CSortClass is almost what I was looking for, and a small addition made it exactly what I wanted.  I'm adding a Windows Explorer type view to a program, and like Explorer, I wanted a CListView that showed folders first, alphabetically, then files, alphabetically.  I managed to do this in two steps; the second is a small addition to your code.
    
    

    First, I made column one of each item show the file size. If the item was a folder, I made column one a blank text string (easy to do by checking the dwFileAttributes member of the WIN32_FIND_DATA I use to fill the CListCtrl).

    Next, I modified your CSortClass constructor to the following code:

    CSortClass::CSortClass(CListCtrl * _pWnd, const int _iCol, const bool _bIsNumeric)
    {
    iCol = _iCol;
    pWnd = _pWnd;
    bIsNumeric = _bIsNumeric;

    ASSERT(pWnd);
    int max = pWnd->GetItemCount();
    DWORD dw;
    CString txt;
    if (bIsNumeric)
    {
    for (int t = 0; t < max; t++)
    {
    dw = pWnd->GetItemData(t);
    txt = pWnd->GetItemText(t, iCol);
    pWnd->SetItemData(t, (DWORD) new CSortItemInt(dw, txt));
    }
    }
    else
    {
    for (int t = 0; t < max; t++)
    {
    dw = pWnd->GetItemData(t);
    txt = pWnd->GetItemText(t, iCol);
    // my added code
    CString txt2 = pWnd->GetItemText(t, 1);
    if(txt2.GetLength() == 0)
    {
    txt2 = txt;
    txt = "\n";
    txt += txt2;
    }
    // end of my added code
    pWnd->SetItemData(t, (DWORD) new CSortItem(dw, txt));
    }
    }
    }

    All my added code does is check for the blank text string in column one (denoting a folder), and if it is found, it adds a newline character to the beginning of the sort column string (only for the sorting function). Since a newline character is always lower than text, the folders are shown first, in alphabetical order, then the files, also in alphabetical order. Clicking any column still shows folders first, then files.

    Reply
  • Excellent! Great solid class!

    Posted by Legacy on 03/20/2003 12:00am

    Originally posted by: Patrick

    This class provides everything I needed for evc++ applications, look above for needed modifications posted by another user!
    Thanks.

    Reply
  • Great job but...

    Posted by Legacy on 02/11/2003 12:00am

    Originally posted by: Simon

    Do you have any clue on how to mark the sorted column with an arrow to see which one is sorted and if the sort is ascendent or not.
    Thanx for this great piece of code.

    Reply
  • It helps me very much, Thank you

    Posted by Legacy on 12/06/2002 12:00am

    Originally posted by: Bahman Ganji

    :) :)

    Reply
  • Excellent Work, I made slighty changes for eVC++

    Posted by Legacy on 07/10/2002 12:00am

    Originally posted by: devchand

    This is excellent class,it makes a work very easy.
    Thank you very much
    Dev

    Reply
  • Good Job. I got working on Microsoft eMbedded VC++ (Pocket PC)

    Posted by Legacy on 05/23/2002 12:00am

    Originally posted by: Mike Dawn

    Hi:

    I want to thank you. I use this code, and modify a little bit for embedded Pocket PC. It work great.

    Mike D.

    Reply
  • Great job!!!

    Posted by Legacy on 05/08/2002 12:00am

    Originally posted by: Morten Strand

    Just what I was looking for. Works great. Thanks again.

    Ragards,
    Morten Strand

    Reply
  • Thank you very much..

    Posted by Legacy on 04/13/2002 12:00am

    Originally posted by: Daniel

    Thank you very much.. ^^;;

    Reply
  • Thans! very useful! *^^*

    Posted by Legacy on 12/26/2001 12:00am

    Originally posted by: kyuchong

    *^^*

    Reply
  • Nice Good! Useful SourceCode Thank You^^

    Posted by Legacy on 09/04/2001 12:00am

    Originally posted by: Lim Ji Soon

    "CListCtrl" hasn't simple Sort-Method~~~~~
    
    

    so, i fall in hell~~....

    but your Source-Code save me~

    Onemore-time Thank you! Good Luck!

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

  • The first phase of API management was about realizing the business value of APIs. This next wave of API management enables the hyper-connected enterprise to drive and scale their businesses as API models become more complex and sophisticated. Today, real world product launches begin with an API program and strategy in mind. This API-first approach to development will only continue to increase, driven by an increasingly interconnected web of devices, organizations, and people. To support this rapid growth, …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds