Have you ever wanted to sort a
TreeView control by trying to inherit from
TreeViewCollection (the data type of the
Nodes property)? After writing an entire subclass doing just this, I tried to compile my work only to find out that there is a problem extending collections, see this MSDN post for more information.
So what now? The forum post offered little in the way of ideas. Without a way to extend the
TreeViewCollection class, the next most logical choice is to put methods in either the
TreeView subclass or in the callee class. Either way, you end up with a method signature that looks like this...
Add(TreeNodeCollection nodes, TreeNode node)
The first thing I didn't like was the two parameter method signature. If I had been able to extend the
TreeViewCollection class (or was not sorting), I would only be passing the new
TreeNode to the
Add method (of the
The choice of putting a method like this in the callee class was the first option to go. I have multiple classes (forms classes in my) writing to the
TreeView control. I also didn't like the choice of placing it in
TreeView control, because essentially it's a
static method (doesn't use any instance variables).
If there was only a way to add methods to the
TreeViewCollection class... After thinking a while, I came up with an idea that is complete syntax candy to accessing the
Nodes property directly. I was inspired by indexers to accomplish what I was looking for. For an introduction to Indexers, check out this tutorial.
Indexers are wonderfully flexible in C-Sharp. Typically, indexers are used to access a particular element of a collection or data set. My idea was to use an index to pass in an existing collection and place it in a wrapper class (the indexer's return type).
Using the Code
First I started by adding a property to my subclass of the
TreeView control. The property was called
SortedNodes and was of type
InsertionSortWrapper is basically the method I was planning to have in the subclass of
TreeViewCollection (with override and virtual modifiers removed). Adding an indexer allowed convenient wrapping of any
Nodes collection using the following syntax...
private void buttonPopulate_Click(object sender, EventArgs e)
for (int i = 0; i < 100; i++)
TreeNode topLevelNode = new TreeNode(RandomString());
for (int j = 0; j < 20; j++)
TreeNode childNode = new TreeNode(RandomString());
This code is made possible by using an indexer, passing the top level
Nodes property to the constructor of
InsertionSortWrapper, and making that your
SortedNodes property in the
TreeView. The code looks like this...
public sealed class InsertionSortWrapper
public InsertionSortWrapper this[TreeNodeCollection collection]
return new InsertionSortWrapper(collection);
private TreeNodeCollection Nodes
public InsertionSortWrapper(TreeNodeCollection actualNodes)
Nodes = actualNodes;
In the demo, I provided an insertion sort implementation. The wrapper provides
Add() methods that have the same signature as in
public methods in the wrapper use the
private Nodes property to do all the real work on the actual nodes; finding the proper place to insert, and inserting the node there. You could implement additional methods or any sorting you choose. While you wouldn't have to use the same signatures as the methods in
TreeViewCollection, it is the primary reason I had for implementing this. Here is the core of the insertion sort...
public int Add(TreeNode node, bool flagOnTop)
node.Tag = FLAG_ONTOP;
node.Tag = FLAG_NONE;
int index = GetInsertionIndex(node);
private int GetInsertionIndex(TreeNode node)
bool newNodeIsOnTop = ((((Int32)node.Tag) & FLAG_ONTOP) > 0);
for (int i = 0; i < Nodes.Count; i++)
TreeNodeCompareResult result =
bool testNodeIsOnTop = ((((Int32)Nodes[i].Tag) & FLAG_ONTOP) > 0);
if (!newNodeIsOnTop && testNodeIsOnTop)
if (newNodeIsOnTop && !testNodeIsOnTop)
if (result == TreeNodeCompareResult.LessThan) continue;
if (result != TreeNodeCompareResult.LessThan) return i;
- 17 November 2009 - Original post