Click here to Skip to main content
11,490,973 members (71,604 online)
Click here to Skip to main content

Tagged as

Sorting a CTreeCtrl

, 5 Oct 2012 CPOL 7.6K 2
Rate this:
Please Sign up or sign in to vote.
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

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.

  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.

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

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;
} 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:
    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 with 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)
         {
            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:

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)

Share

About the Author

Marius Bancila
Software Developer (Senior) Visma Software
Romania Romania
Marius Bancila is a Microsoft MVP for VC++. He works as a software developer for Visma, a Norwegian-based company. He is mainly focused on building desktop applications with VC++ and VC#. He keeps a blog at http://www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++ programmers.
Follow on   Twitter

Comments and Discussions

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

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

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

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