Click here to Skip to main content
13,150,867 members (46,833 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as


7 bookmarked
Posted 5 Oct 2012

Sorting a CTreeCtrl

, 5 Oct 2012
Rate this:
Please Sign up or sign in to vote.
How to sort a CTreeCtrl

The CTreeCtrl supports several ways to sort its content:

  • InsertItem allows to insert the child item alphabetically, when specifying TVI_SORT for hInsertAfter
  • SortChildren performs an alphabetical sorting of the child items of the given parent item in a tree
  • SortChildrenCB performs a sort with a user-defined callback (hence the CB suffix) of the children of the specified item

Let’s consider this tree and investigate these two sorting methods.

Alphabetical Sorting

SortChildren() does an alphabetical sorting. It is important to note that SortChildren() does not work recursively. It only sorts the children of the specified item. Therefore, the following call would only sort the immediate children of the root item.


To sort the entire tree, one needs to traverse the tree and call SortChildren for every item that has children (actually only items with more than one child need sorting). The following method performs a depth-first traversal and sorts all nodes that have children.

void CTreeSortDemoDlg::SortItem(HTREEITEM item)
   if(item != NULL)
      if(item == TVI_ROOT || m_tree.ItemHasChildren(item))
         HTREEITEM child = m_tree.GetChildItem(item);

         while(child != NULL)
            child = m_tree.GetNextItem(child, TVGN_NEXT);


// ...

The result for the given tree is:

User Defined Sorting

SortChildrenCB() allows the user to set a callback functions that the framework calls each time it needs to compare two items to perform the sorting. This allows to customize the sorting. Just like SortChildren(), this method only sorts the children of the specified item, and does not perform a recursive sorting of the entire sub-tree.

This method has a single argument, a pointer to a TVSORTCB structure, that is defined like this:

typedef struct tagTVSORTCB {
  HTREEITEM    hParent;
  PFNTVCOMPARE lpfnCompare;
  LPARAM       lParam;

The fields have the following meaning:

  • hParent: is the item whose children are to be sorted
  • lpfnCompare: a pointer to the user-defined callback function that does the sorting
  • lParam: is the value that is passed as to the 3rd parameter of the callback function, which looks like this:
    int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);

    Usually, this parameter is a pointer to the tree itself, so that you can retrieve information about the items in the callback if necessary. However, it can be anything, including NULL.

    The lParam1 and lParam2 parameters correspond to the lParam member of the TVITEM structure for the two items being compared.

    The callback function must return a negative value if the first item should precede the second, a positive value if the first item should follow the second, or zero if the two items are equivalent.

Suppose we set for each item a pointer to a structure that looks like this:

struct ItemData
   CString  Name;
   int      Value;

   CString ToString() const
      CString str;
      str.Format(_T("%s = %d"), Name, Value);
      return str;

ItemData* data = MakeData(base, prefix);
HTREEITEM item = m_tree.InsertItem(data->ToString(), parent);
m_tree.SetItemData(item, (DWORD_PTR)data);

A user defined callback (a class method declared static) defines the precedence between two items based on the Value field.

int CALLBACK CTreeSortDemoDlg::CustomCompare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
   ItemData* data1 = reinterpret_cast<ItemData*>(lParam1);
   ItemData* data2 = reinterpret_cast<ItemData*>(lParam2);

   return (data1 != NULL && data2 != NULL) ? (data1->Value > data2->Value) : 0;

Sorting the entire tree with this callback is similar to the previous recursive method. The call to SortChildren() is replaced with a call to SortChildrenCB().

void CTreeSortDemoDlg::CustomSortItem(HTREEITEM item)
   if(item != NULL)
      if(item == TVI_ROOT || m_tree.ItemHasChildren(item))
         HTREEITEM child = m_tree.GetChildItem(item);

         while(child != NULL)
            child = m_tree.GetNextItem(child, TVGN_NEXT);

         TVSORTCB tvs;
         tvs.hParent = item;
         tvs.lpfnCompare = &CTreeSortDemoDlg::CustomCompare;
         tvs.lParam = reinterpret_cast<LPARAM>(&m_tree);


// ...

The result on the given example is:

For a full sample, see TreeSortDemo (158).


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Marius Bancila
Architect Visma Software
Romania Romania
Marius Bancila is the author of Modern C++ Programming Cookbook. He was a Microsoft MVP for VC++ and later Visual Studio and Development Technologies for 11 years. He works as a system architect for Visma, a Norwegian-based company. He is mainly focused on building desktop applications with VC++ and VC#. He keeps a blog at, focused on Windows programming. He is the co-founder of, a community for Romanian C++ programmers.

You may also be interested in...

Comments and Discussions

QuestionlpfnCompare Problem Pin
rbrunton8-Jun-14 3:58
memberrbrunton8-Jun-14 3:58 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170924.2 | Last Updated 5 Oct 2012
Article Copyright 2012 by Marius Bancila
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid