Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Sorting a CTreeCtrl

4.54/5 (7 votes)
5 Oct 2012CPOL2 min read 23.2K  
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.

Image 1

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.

C++
m_tree.SortChildren(TVI_ROOT);

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.

C++
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)
         {
            SortItem(child);
            child = m_tree.GetNextItem(child, TVGN_NEXT);
         }

         m_tree.SortChildren(item);
      }
   }
}

// ...
SortItem(TVI_ROOT);

The result for the given tree is:

Image 2

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:

C++
typedef struct tagTVSORTCB {
  HTREEITEM    hParent;
  PFNTVCOMPARE lpfnCompare;
  LPARAM       lParam;
} TVSORTCB, *LPTVSORTCB;

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:
    C++
    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:

C++
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.

C++
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().

C++
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)
         {
            CustomSortItem(child);
            child = m_tree.GetNextItem(child, TVGN_NEXT);
         }

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

         m_tree.SortChildrenCB(&tvs);
      }
   }
}

// ...
CustomSortItem(TVI_ROOT);

The result on the given example is:

Image 3

For a full sample, see TreeSortDemo (158).

License

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