Determining row indices in Sorting Comparison function

The Sorting mechanism ( SortItems() ) of CListCtrl, does not provide us with the row indices inside the Comparison function. Without the row indices, the easiest way to get to our data inside the function is to store the text data in memory and set it's pointer as the LPARAM for each row. If the requirement is to sort the ListCtrl based on the item text alone, we shouldn't have to waste this memory. In such cases the following code can be used. It uses the FindItem() to determine the row index by using the LPARAM passed to the comparison function. Once we have the row index, we can use the GetItemText() to get the text. The pre-requisite of using this method is to set the LPARAM of each row to a unique value - a simple serial number will do. Compared to the sorting routines based on QuickSort provided by many in this site, this method was faster upto 5000 rows. After this threshold the FindItem() becomes slower and the custom sorting routines were much more faster.


/////////////////////////////////////////////////////////////////////////////
// SortInfo structure definition
typedef struct tagSortInfo
{
	CListCtrl * pListControl;
	int	nColumnNo;
	bool nAscendingSortOrder;
}SortInfo;

/////////////////////////////////////////////////////////////////////////////
// CTestDialog dialog

class CTestDialog : public CDialog
{
public:
	CTestDialog(CWnd* pParent = NULL);   // standard constructor
//Dialog Data
	//{{AFX_DATA(CTestDialog)
	enum { IDD = IDD_DIALOG1 };
	CListCtrl	m_ListControl;
	//}}AFX_DATA
// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CTestDialog)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

	static int CALLBACK SortList(LPARAM lpOne, LPARAM lpTwo, LPARAM lpArg);
// Implementation
	SortInfo m_SortInfo;
protected:
	// Generated message map functions
	//{{AFX_MSG(CTestDialog)
	//}}AFX_MSG
	afx_msg void OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult);
	
	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////
//
void CTestDialog::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
	HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
	if(phdn->iButton == 0)
	{
		if (phdn->iItem == m_SortInfo.nColumnNo)
			m_SortInfo.nAscendingSortOrder = !m_SortInfo.nAscendingSortOrder;
		else
			m_SortInfo.nAscendingSortOrder = TRUE;

		m_SortInfo.nColumnNo = phdn->iItem;
		m_SortInfo.pListControl = &m_ListControl;

		m_ListControl.SortItems( SortList,(DWORD)&m_SortInfo );
	}
	*pResult = 0;
}

int CALLBACK CTestDialog::SortList(LPARAM lpOne, LPARAM lpTwo, LPARAM lpArg)
{
	int nResult 			= 0;
	SortInfo * pSortInfo 		= (SortInfo *) lpArg;

	int nColumnNo 			= pSortInfo->nColumnNo;
	CListCtrl * pListControl 	= pSortInfo->pListControl;
	bool nAscendingSortOrder 	= pSortInfo->nAscendingSortOrder;

	int lFirstData = -1, lSecondData = -1;

	LV_FINDINFO FindInfo;
	// use LVFI_WRAP for cases where lpTwo represents a row before lpOne
	FindInfo.flags = LVFI_PARAM | LVFI_WRAP;
	FindInfo.lParam = lpOne;
	lFirstData = pListControl->FindItem(&FindInfo);

	FindInfo.lParam = lpTwo;
	// reduce searching time by setting the start row as lFirstData
	lSecondData = pListControl->FindItem(&FindInfo,lFirstData);
	// because we are searching for LPARAM sent to us, FindItem() on 
	// these values should always be successful
	ASSERT(lFirstData != -1); ASSERT(lSecondData != -1);

	CString FirstText = pListControl->GetItemText(lFirstData,nColumnNo);
	CString SecondText = pListControl->GetItemText(lSecondData,nColumnNo);
	
	int nCompareValue = FirstText.Compare(SecondText);
	nResult = nCompareValue * ((nAscendingSortOrder)?1:-1);
	// or
	// your own comparison code

	return nResult;
}

Last updated: 5 August 1998



Comments

  • For Beginners as me - Important!

    Posted by elk11 on 02/16/2006 09:30am

    To make this brilliant piece of source work add
    
    ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked) 
    ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
    
    to your Message Map! Do not use the Wizard to insert these lines, it will offer you only HDN_ITEMCLICK, but you need HDN_ITEMCLICKA and HDN_ITEMCLICKW...

    Reply
  • Thanks

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

    Originally posted by: Aaron J

    Excellent article. The procedure to find the items was just what I was looking for.

    Reply
  • Nice Job ! Thank You !

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

    Originally posted by: Jack Porter

    Clearer and more thorough than the MSDN example.

    Reply
  • Important!

    Posted by Legacy on 02/18/2002 12:00am

    Originally posted by: Stefan

    "... is to set the LPARAM of each row to a unique value" is very important!! If you use this code don't forget to set the LPARAM of each element using the SetItemData(item, UNIQUE_LPARAM) function.
    

    Reply
  • Microsoft needs your help

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

    Originally posted by: Nat Pennathur

    I struggled with code samples I got from Microsoft documentation, that had a wierd sorting algorithm happening. Only MS could have conceived one. Copied your code as is, and worked like a charm. Thanks a mil !!!

    Reply
  • SORT WAS NOT DONE.

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

    Originally posted by: young joon hong

    I have a good look at "Determining row indices in SortItems() Comparison 
    
    function" in www.codeguru.com.
    However, I found a trouble when implement the program - SORT WAS NOT DONE.

    I am very sorry for your inconvenience caused by, but after check my program and let me know the trouble point or please send the whole program by compressed file.
    Thanking for your kind cooperations in advance and looking forward to your reply by return.

    Reply
  • Thank you very much!!

    Posted by Legacy on 11/29/2001 12:00am

    Originally posted by: Jay Lee

    Thank you for this source code...
    With this code, I can solve my problems...
    But when I use this code...
    First time this was not worked...
    so, I edited "phdn->iItem" to "phdn->iButton".
    Is it right??
    If this editing was wrong, where i did mistake??

    Reply
  • Thank you!

    Posted by Legacy on 10/15/2001 12:00am

    Originally posted by: J. Swindler

    I had been struggling with sorting my list control items by column using the callback function method that you described, and was not having much success. I couldn't find any Microsoft documentation that said you had to find each item in the list rather than use the lparam1 and lparam2 arguments passed into the callback function. However, you explained it very well and now my control works perfectly. Great job.

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

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds