4

I am having trouble sorting child nodes of a treeview in my winforms program. My treeview is populated by some XML files and it uses an internal text inside the xml files as Text property of nodes (So I think I cant sort them before adding them to the tree view, or if it is possible, since the xml files are big in size I dont want to waste the process). A populated treeview in my program looks like this:

enter image description here

As you can guess I want child nodes to sort like (I dont want HBM\D10 to come after HBM\D1) rather I want:

    HBM\D1
    HBM\D2
    HBM\D3
etc...

I have already tried treeView1.Sort() and also adding beginUpdate and endUpdate but I had no suceess :(

I am using .NET 4, any tips would be appriciated

ok I sortet it out using Thomas's advice:

    class NodeSorter : IComparer
{
        public int Compare(object x, object y) 
        {         
            TreeNode tx = (TreeNode)x; 
            TreeNode ty = (TreeNode)y;

            if (tx.Text.Length < ty.Text.Length)
            {
                return -1;
            }

            if (tx.Text.Length > ty.Text.Length)
            {
                return 1;
            }

            return 0;
        } 
}
Dumbo
  • 13,555
  • 54
  • 184
  • 288

4 Answers4

11

You need to create a custom comparer and assign it to the TreeViewNodeSorter property:

public class NodeSorter : System.Collections.IComparer
{
    public int Compare(object x, object y)
    {
        TreeNode tx = (TreeNode)x;
        TreeNode ty = (TreeNode)y;

        // Your sorting logic here... return -1 if tx < ty, 1 if tx > ty, 0 otherwise
        ...
    }
}


...

treeView.TreeViewNodeSorter = new NodeSorter();
treeView.Sort();
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Thanks, would you mind giving a bit more about the logic part? is it about converting nodes to binary or something? – Dumbo Apr 11 '11 at 08:27
  • @Sean87, you need to sort by the text of the TreeNode according to your own logic, i.e. extract the number part of the text, and compare by number if the rest of the name is equal – Thomas Levesque Apr 11 '11 at 09:24
  • In order for this to work on .net 4(Windows 8) I had to make it inherit from Comparer and implement the Compare as `public override int Compare(TreeNode Node1, TreeNode Node2)` – ThunderGr Oct 17 '13 at 12:30
  • @ThunderGr, there is no reason to do that. Even in .NET 4.5, the `TreeViewNodeSorter` property is till of type `IComparer`, not `IComparer`. I suspect you were just missing a namespace import... The code I posted works with .NET 4.x in the same way as with earlier versions. – Thomas Levesque Oct 17 '13 at 15:07
  • I Use System.Collections.Generic and System.Windows.Forms(of course). The compiler did not let me derive from IComparer, and it would not assign IComparer to the TreeViewNodeSorter. After I changed it as stated, it accepted it without any fuss. Perhaps you could include the relevant using statement required for your example to work in your answer? – ThunderGr Oct 18 '13 at 05:51
  • @ThunderGr, you should import `System.Collections` (not `.Generic`) – Thomas Levesque Oct 18 '13 at 08:11
  • Done. Works like a charm. Thank you. If you added it on the answer or, at least, mention it is required, it would be more complete. System.Collections is not added by default in the IDE, when you create a new form. – ThunderGr Oct 18 '13 at 08:56
  • 1
    @ThunderGr, I fixed the code to specify the namespace explicitly – Thomas Levesque Oct 18 '13 at 09:09
2

You're using alphabetic sorting, so D10 comes after D1.
You should try to sort discarding "D" char and converting the rest of string to a number.

Marco
  • 56,740
  • 14
  • 129
  • 152
2

I've written some custom comparers to make creating the comparer you need here somewhat easier to do: MultiComparer and ProjectionComparer. Together, you could create a comparer to sort what you need on the fly without having to create a class by hand. What I provide here isn't actually how I have the classes written, I trimmed off a lot of code for brevity (though left some helpers to be easier to use).

To create the comparer:

var comparer = OrderedComparer.Create(
    ProjectionComparer.Create((TreeNode tn) => tn.Text.Substring(0, 1)),
    ProjectionComparer.Create((TreeNode tn) => Convert.ToInt32(tn.Text.Substring(1)))
);
treeView.TreeViewNodeSorter = comparer;

And the classes:

public static class OrderedComparer
{
    public static OrderedComparer<TSource> Create<TSource>(params IComparer<TSource>[] comparers)
    { return new OrderedComparer<TSource>(comparers); }
}
public static class ProjectionComparer
{
    public static ProjectionComparer<TSource, TKey> Create<TSource, TKey>(Func<TSource, TKey> keySelector)
    { return new ProjectionComparer<TSource, TKey>(keySelector); }
}
public sealed class OrderedComparer<TSource> : Comparer<TSource>
{
    public OrderedComparer(params IComparer<TSource>[] comparers)
    {
        this.comparers = comparers.ToArray();
    }
    private IComparer<TSource>[] comparers;

    public override int Compare(TSource x, TSource y)
    {
        var cmp = 0;
        foreach (var comparer in comparers)
            if ((cmp = comparer.Compare(x, y)) != 0)
                break;
        return cmp;
    }
}
public sealed class ProjectionComparer<TSource, TKey> : Comparer<TSource>
{
    public ProjectionComparer(Func<TSource, TKey> keySelector)
    {
        this.keySelector = keySelector;
        this.keyComparer = Comparer<TKey>.Default;
    }
    private Func<TSource, TKey> keySelector;
    private IComparer<TKey> keyComparer;

    public override int Compare(TSource x, TSource y)
    {
        var xKey = keySelector(x);
        var yKey = keySelector(y);
        return keyComparer.Compare(xKey, yKey);
    }
}
Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
0

Following is the solution I have used in my current project.

public class NodeSorter : IComparer
{
   public int Compare(object x, object y)
   {
      TreeNode tx = x as TreeNode;
      TreeNode ty = y as TreeNode;
      if (tx.Name== null || ty.Name== null)
         return 0;
      return (-1) * string.Compare(tx.Name.ToString(), ty.Name.ToString());
   }
} 

tvListofItems.TreeViewNodeSorter = new NodeSorter();
tvListofItems.Sort();              
jo_Veera
  • 293
  • 3
  • 12