13,150,867 members (46,833 online)
Technical Blog
Add your own
alternative version

#### Stats

12.7K views
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.

```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 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)
{
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)

## About the Author

 Architect Visma Software 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 http://www.mariusbancila.ro/blog, focused on Windows programming. He is the co-founder of codexpert.ro, a community for Romanian C++ programmers.

## Comments and Discussions

 First Prev Next
 lpfnCompare Problem rbrunton8-Jun-14 3:58 rbrunton 8-Jun-14 3:58
 Last Visit: 31-Dec-99 18:00     Last Update: 25-Sep-17 21:39 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    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