Click here to Skip to main content
Click here to Skip to main content

Wrapping a Treeview's Nodes Property to Provide Extra Functionality (Such as Sorting)

, 20 Nov 2009 Ms-PL
Rate this:
Please Sign up or sign in to vote.
Workarounds for not being able to inherit from TreeViewCollection, and for having a read-only Nodes property

Introduction

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.

Background

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

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

// This code compares traditional usage with wrapper usage
private void buttonPopulate_Click(object sender, EventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        TreeNode topLevelNode = new TreeNode(RandomString());
        // Replacement for... sortedTreeView.Nodes.Add(topLevelNode);
        sortedTreeView.SortedNodes.Add(topLevelNode);
        for (int j = 0; j < 20; j++)
        {
            TreeNode childNode = new TreeNode(RandomString());
            // Replacement for... topLevelNode.Nodes.Add(childNode);
            sortedTreeView.SortedNodes[topLevelNode.Nodes].Add(childNode);
        }
    }
} 

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]
    {
        get
        {
            return new InsertionSortWrapper(collection);
        }
    }

    private TreeNodeCollection Nodes
    {
        get;
        set;
    }

    public InsertionSortWrapper(TreeNodeCollection actualNodes)
    {
        Nodes = actualNodes;
    }

    // Several Add() methods and compare functions (not shown here)
} 

In the demo, I provided an insertion sort implementation. The wrapper provides Add() methods that have the same signature as in TreeViewCollection. The 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)
{
    // The flagOnTop parameter will insert the new TreeNode before
    // any other TreeNodes that do not have that flag set.
    if (flagOnTop)
        node.Tag = FLAG_ONTOP;
    else
        node.Tag = FLAG_NONE;
    // Get Insertion Index
    int index = GetInsertionIndex(node);
    Nodes.Insert(index, node);
    return index;
}

private int GetInsertionIndex(TreeNode node)
{
    bool newNodeIsOnTop = ((((Int32)node.Tag) & FLAG_ONTOP) > 0);
    for (int i = 0; i < Nodes.Count; i++)
    {
        TreeNodeCompareResult result =
            TreeNodeCompare(Nodes[i].Text, node.Text);
        bool testNodeIsOnTop = ((((Int32)Nodes[i].Tag) & FLAG_ONTOP) > 0);
        if (!newNodeIsOnTop && testNodeIsOnTop)
            continue;
        if (newNodeIsOnTop && !testNodeIsOnTop)
            return i;
        if (result == TreeNodeCompareResult.LessThan) // <
            continue;
        if (result != TreeNodeCompareResult.LessThan) // >=
            return i;
    }
    return Nodes.Count;
} 

History

  • 17 November 2009 - Original post

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

J.Guyette
Software Developer Viipe.com
United States United States
C++ programmer, 1994 to 2009
 
Java programmer, 2006 to 2008
 
C-Sharp programmer, 2007 to 2009
 
I am the owner, operator, lead programmer, and lead web developer of Viipe.com (2009)
 
Viipe.com is a small software engineering company. Our first product, VKO, is a macro program with an embedded LUA scripting language.

Comments and Discussions

 
Generalone question and a small suggestion [modified] PinmemberBillWoodruff21-Nov-09 22:18 
GeneralRe: one question and a small suggestion PinmemberJ.Guyette24-Nov-09 16:30 

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
Web02 | 2.8.1411023.1 | Last Updated 20 Nov 2009
Article Copyright 2009 by J.Guyette
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid