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).