Search code examples
c#winforms

Sort specific Node in TreeView?


I would like to know if there is any method to sort (A-Z) a specific node of a TreeView.

The node I want to order is the node "Node1 \ z"

To display it like this:

H N Y Z

treeview image

Thank you


Solution

  • Once you call the TreeView.Sort method, you actually apply the sorted TREEVIEWSTATE through the setter of the hidden Sorted property. The control by that sorts all the nodes using the default sorter, the ascending alphabetical sort. Any attempts afterwards to sort the nodes in a different way fail. You'll note that nothing happens when you remove, sort, and reinsert child nodes of a specific node because once you insert them, the default sorter will interfere and revert your different sort. Again, all the mentioned is when you call the .Sort method before any other sorting routines.

    To override this behavior, you need to provide a custom node sorter for the TreeView.TreeViewNodeSorter property. An example that allows you to sort the tree or children of node in ascending or descending orders.

    public class TreeNodeComparer : IComparer
    {
        public TreeNodeComparer(SortOrder sortOrder = SortOrder.Ascending) : base()
        {
            SortOrder = sortOrder;
        }
    
        public int Compare(object x, object y)
        {
            var xn = x as TreeNode;
            var yn = y as TreeNode;
    
            switch (SortOrder)
            {
                case SortOrder.Descending:
                    return string.Compare(xn.Text, yn.Text) * -1;
                case SortOrder.Ascending:
                    return string.Compare(xn.Text, yn.Text);
                default:
                    return 1;
            }
        }
    
        public SortOrder SortOrder { get; set; } = SortOrder.Ascending;
    }
    

    Note return 1; here in case SortOrder.None is necessary to not revert sorting the child nodes of a specific node.

    A couple of extension methods for the TreeView and TreeNode types.

    .NET Framework 4.8.1, c# 7.3

    public static class TreeViewExtensions
    {
        public static void Sort(this TreeView self, SortOrder order = SortOrder.Ascending)
        {
            self.TreeViewNodeSorter = new TreeNodeComparer(order);
            self.Sort();
        }
    
        public static void Sort(this TreeNode self, SortOrder order = SortOrder.Ascending)
        {
            TreeNode[] tmp;
            TreeView tv = self.TreeView;
    
            if (order == SortOrder.Descending)
                tmp = self.Nodes.Cast<TreeNode>().OrderByDescending(n => n.Text).ToArray();
            else
                tmp = self.Nodes.Cast<TreeNode>().OrderBy(n => n.Text).ToArray();
    
            var sorter = tv.TreeViewNodeSorter as TreeNodeComparer ?? new TreeNodeComparer();
            sorter.SortOrder = SortOrder.None;
    
            tv.TreeViewNodeSorter = sorter;
            tv.BeginUpdate();
            self.Nodes.Clear();
            self.Nodes.AddRange(tmp);
            tv.EndUpdate();
        }
    }
    

    .NET8+, c# 12+

    public class TreeNodeComparer(SortOrder sortOrder = SortOrder.Ascending) : IComparer
    {
        public int Compare(object? x, object? y)
        {
            if (x is not TreeNode xn || y is not TreeNode yn) return 0;
    
            return SortOrder switch
            {
                SortOrder.Descending => string.Compare(xn.Text, yn.Text) * -1,
                SortOrder.Ascending => string.Compare(xn.Text, yn.Text),
                _ => 1,
            };
        }
    
        public SortOrder SortOrder { get; set; } = sortOrder;
    }
    
    public static class TreeViewExtensions
    {
        public static void Sort(this TreeView self, SortOrder order = SortOrder.Ascending)
        {
            self.TreeViewNodeSorter = new TreeNodeComparer(order);
            self.Sort();
        }
    
        public static void Sort(this TreeNode self, SortOrder order = SortOrder.Ascending)
        {
            if (self.TreeView is null) return;
            TreeNode[] tmp;
            TreeView tv = self.TreeView;
    
            if (order == SortOrder.Descending)
                tmp = [.. self.Nodes.Cast<TreeNode>().OrderByDescending(n => n.Text)];
            else
                tmp = [.. self.Nodes.Cast<TreeNode>().OrderBy(n => n.Text)];
    
            var sorter = tv.TreeViewNodeSorter as TreeNodeComparer ?? new TreeNodeComparer();
            sorter.SortOrder = SortOrder.None;
    
            tv.TreeViewNodeSorter = sorter;
            tv.BeginUpdate();
            self.Nodes.Clear();
            self.Nodes.AddRange(tmp);
            tv.EndUpdate();
        }
    }
    

    You can call them as follows:

    // To sort the whole thing...
    YourTreeView.Sort(SortOrder.Descending);
    
    // Or the children of the selected node for example...
    YourTreeView.SelectedNode.Sort(SortOrder.Ascending);
    

    SO73210073