Click here to Skip to main content
15,886,518 members
Articles / Programming Languages / C++
Article

New ListCtrl Sorting Algorithm in XP

Rate me:
Please Sign up or sign in to vote.
4.73/5 (18 votes)
27 Apr 20072 min read 59.4K   2.1K   42   12
Sorting items more naturally in ListCtrl, like XP does. Adding a single line into your program to enable this cool feature.

Screenshot - sortlistctrlxp.gif

Introduction

There's a new feature for ListCtrl in Windows XP which most of us, maybe, didn't notice. There is a new sorting algorithm in ListCtrl - to be more accurate, the new comparing algorithm. When you try to sort something, say, COM1 ~ COM11, previous versions of Windows did it like this:

COM1
COM10
COM11
COM2
COM3
..

While Windows XP sorts them this way:

COM1
COM2
COM3
..
COM10
COM11

That's what we excepted.

A nice feature, eh? But Microsoft didn't provide API or something for you to use the new feature in your own programs. So, I developed an algorithm to achieve this, and provided a few classes which you can integrate into your project smoothly. Both MFC and WTL are supported. First, let's check out how to use them.

Using the code

Using the class is quite easy. In an MFC project, first add ListCtrlSort.h, and ListCtrlSort.cpp into your project. Switch to ListViewSort when you use CListView, or ListCtrlSort when using CListCtrl.

As for WTL, it's not necessary or able to add ListCtrlSort.cpp to your project, because it's for MFC only. Just include the ListCtrlSort.h when needed, and change your code like this:

C++
class CWtlListViewSortDemoView : 
    public CWindowImpl<CWtlListViewSortDemoView, CListViewCtrl>, 
    public ListCtrlSortHelper<cwtllistviewsortdemoview>    // add a base class
{
    ...
};

class CMainFrame : public CFrameWindowImpl<cmainframe>, 
    public CUpdateUI<CMainFrame>,
    public CMessageFilter, public CIdleHandler
{
public:
    CWtlListViewSortDemoView m_view;
    BEGIN_MSG_MAP(CMainFrame)
        NOTIFY_CODE_HANDLER(LVN_COLUMNCLICK, m_view.OnColumnClick_)    
        // add a notify message handler
        CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
        CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
    END_MSG_MAP()
    ...
}</cmainframe></cwtllistviewsortdemoview>

All you have to do is make your WTL view inherit ListCtrlSortHelper, and add a notify message handler. That's it.

Refer to the demo projects if you have any problems.

Points of Interest

The classes offer a clean interface to use easily. But what happened behind the scene? The algorithm core is the ListCtrlSortHelper::Compare() function, as shown below:

C++
static int Compare(CString const& csText1, CString const& csText2)
{
    static CRegexp reg(MATCH_NUMBERS);
    int nEnd1 = 0, nEnd2 = 0;

    while(1)
    {
        MatchResult ret1 = reg.Match(csText1, nEnd1);
        MatchResult ret2 = reg.Match(csText2, nEnd2);

        CString const& csSubText1 = ret1.IsMatched() ? csText1.Mid(nEnd1, 
            ret1.GetStart() - nEnd1) : csText1;
        CString const& csSubText2 = ret2.IsMatched() ? csText2.Mid(nEnd2, 
            ret2.GetStart() - nEnd2) : csText2;

        if (csSubText1 != csSubText2)
            return csSubText1 > csSubText2 ? 1 : -1;

        if (! ret1.IsMatched() && ! ret2.IsMatched()) return 0;

        if (ret1.IsMatched() && ret2.IsMatched())
        {
            if (ret1.GetGroupStart(1) >= 0 || ret2.GetGroupStart(1) >= 0)
            {
                double dNum1 = _tcstod(csText1.Mid(ret1.GetStart(), 
                    ret1.GetEnd() - ret1.GetStart()), 0);
                double dNum2 = _tcstod(csText2.Mid(ret2.GetStart(), 
                    ret2.GetEnd() - ret2.GetStart()), 0);
                if (dNum1 != dNum2) return dNum1 > dNum2 ? 1 : -1;
            }
            else
            {
                __int64 nNum1 = _ttoi64(csText1.Mid(ret1.GetStart(), 
                    ret1.GetEnd() - ret1.GetStart()));
                __int64 nNum2 = _ttoi64(csText2.Mid(ret2.GetStart(), 
                    ret2.GetEnd() - ret2.GetStart()));
                if (nNum1 != nNum2) return nNum1 > nNum2 ? 1 : -1;
            }

            nEnd1 = ret1.GetEnd();
            nEnd2 = ret2.GetEnd();
        }
        else
        {
            return ret1.IsMatched() ? 1 : -1;
        }
    }
    return 0;
}

Since the algorithm is not complicated, I will not explain it in detail. Regular expressions are used to distinguish numbers from text. Instead of using the famous boost::Regex (for not everyone installed the huge lib), I adopt a tiny CRegexp lib - deelx, only a single header file is needed. You can go to this site for more information about it. Thanks to sswater shi.

ListCtrlSortHelper sorts items by sending LVM_SORTITEMSEX which offer two nItems instead of two ItemData, like LVM_SORTITEMS does, in the callback which can be passed to GetItemText() to obtain the item text for comparing. Since LVM_SORTITEMSEX is only supported on Windows 2000 or later, these classes have this limitation too.

If your programs run on Windows XP and have a manifest file or resources, congratulations! You've got the bonus feature - sorted column highlighting. Did you notice the dark background of the sorted "COM port" column in the sample image? This has been done by a single SendMessage():

C++
SendMessage(pT->m_hWnd, LVM_FIRST + 140/*LVM_SETSELECTEDCOLUMN*/, 
    nSortColumn_, 0);

That's all. Hope you enjoy it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
MinQuanRen1115-Apr-11 0:35
MinQuanRen1115-Apr-11 0:35 
GeneralRe: My vote of 3 Pin
Member 115352084-Nov-17 15:52
Member 115352084-Nov-17 15:52 
Generalquestion Pin
thready15-Jan-10 7:48
thready15-Jan-10 7:48 
GeneralRe: question Pin
thready15-Jan-10 7:54
thready15-Jan-10 7:54 
GeneralRe: question Pin
thready15-Jan-10 7:58
thready15-Jan-10 7:58 
GeneralBadly named and with ?Chinese? comments on the source Pin
Panic2k33-Dec-08 16:59
Panic2k33-Dec-08 16:59 
GeneralRe: Badly named and with ?Chinese? comments on the source Pin
Dogby8-Jun-09 21:53
Dogby8-Jun-09 21:53 
GeneralRe: Badly named and with ?Chinese? comments on the source Pin
Panic2k324-Jun-09 9:14
Panic2k324-Jun-09 9:14 
GeneralStrCmpLogicalW Pin
Jim Barry30-Apr-07 15:00
Jim Barry30-Apr-07 15:00 
Actually, Microsoft did provide an API for this. Take a look at StrCmpLogicalW.

Jim Barry, MVP (Windows SDK)

GeneralRe: StrCmpLogicalW Pin
nicoster2-May-07 5:20
nicoster2-May-07 5:20 
GeneralVery interesting Pin
Hans Dietrich27-Apr-07 11:10
mentorHans Dietrich27-Apr-07 11:10 
GeneralRe: Very interesting Pin
nicoster27-Apr-07 16:26
nicoster27-Apr-07 16:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.