1

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

IVSoftware
  • 5,732
  • 2
  • 12
  • 23
Fr4nKUwu
  • 13
  • 3
  • You can call `TreeView.Sort`. It's a lot easier if you insert in the right order in the first place. `var ind = parent.Nodes.IndexOfKey(newNodeName); if (ind == -1) parent.Nodes.Add(newNode); else parent.Nodes.Insert(ind, newNode);` – Charlieface Aug 02 '22 at 16:02
  • @Hans: The last time I looked it had. [Here](https://stackoverflow.com/questions/50922465/how-to-sort-the-child-nodes-of-treeview/50923830#50923830) is how to sort in a (somewhat) more advanced way. But to sort only a certain node one could remove it to a new dummy TV and after sorting re-insert or, maybe hack the comparer method to ignore other nodes.. – TaW Aug 02 '22 at 16:59
  • @Fr4nKUwu you _say_ to display it like H N Y **Z** but then you _show_ the available nodes as **X** N Y H and there seems to be a mismatch. Is this just a typo? If so, please consider a quick edit to your post. Thx :) – IVSoftware Aug 03 '22 at 18:19

2 Answers2

1

Your question is how to apply an alpha sort (A-Z) to a specific single node in a TreeView.

before and after sort node z descending

There are many ways to do this and the comments mention some good ones. Here is a solution that efficiently locates the target node using its fully-qualified path in the tree hierarchy. If found, it applies an individualized sort method (specified at runtime) to its children. But first, it must ensure that the TreeView will permit such reordering by setting its Sorted property to false. An advantage of this approach is that it avoids making a call the Sort method for the entire TreeView because that would add an unnecessary layer of complexity.


Find the target node

Given a rule for the TreeView that the fully-qualified path to any node (for example, @"Node1\z") is unique, the target can be obtained very efficiently by making an extension for TreeView that returns the specified node (or null if not found).

public static TreeNode Find(this TreeView treeView, string path)
{
    var parse = path.Split('\\');
    var nodes = treeView.Nodes;
    TreeNode node = null;
    foreach (var text in parse)
    {
        node = nodes.Cast<TreeNode>().FirstOrDefault(node => node.Text == text);
        if (node == null) break;
        nodes = node.Nodes;
    }
    return node;
}

Sort children of target node

This extension for TreeNode sorts its children according to the comparer Func passed in as an argument.

public static void Sort(
    this TreeNode node,
    Func<TreeNode, TreeNode, int> sorter)
{
    // Make sure the TreeView will allow reordering
    if (node.TreeView != null)
    {
        node.TreeView.Sorted = false;
    }
    // Copy the nodes to a list
    var list = node.Nodes.Cast<TreeNode>().ToList();
    // Sort the list however the `Sorter` says to.
    list.Sort((a, b) => sorter(a, b));
    // Clear the 'old' order
    node.Nodes.Clear();
    // Install the 'new' order
    foreach (var sorted in list)
    {
        node.Nodes.Add(sorted);
    }
}

Complete method to sort (A-Z) a specific node of a TreeView.

So, to answer the question Is there any method to sort a specific node of a TreeView from A-Z let's say we manufacture such a method by making an extension for TreeView that does just that. For the path argument specify the entire hierarchal path like @"Node1" or @"Node1\z".

public static bool SortIfNodeFound(
    this TreeView treeView, 
    string path, 
    Func<TreeNode, TreeNode, int> sorter)
{
    var node = treeView.Find(path);
    node?.Sort(sorter);
    return node != null;
}
IVSoftware
  • 5,732
  • 2
  • 12
  • 23
  • [Clone](https://github.com/IVSoftware/sort_specific_node.git) sample. – IVSoftware Aug 03 '22 at 14:07
  • Add another `TreeNode` with some children, set it's Text property `z` and try. What if this new node is the target node? Or should the predicate process all the matches? The sort order is a good add through. – dr.null Aug 03 '22 at 15:38
  • 1
    Yes, of course you are right that the node would have to be uniquified, maybe with a guid in the `Tag` property or what not. That's the added benefit of the `Predicate` property is you can match it however you need to. (Are we having w-a-a-a-y too much fun with this?) – IVSoftware Aug 03 '22 at 15:42
  • @dr.null I though about what you said and it leads to a much more optimal solution so thx!. Basically, the easiest way to uniquify to nodes is to have a rule that _fully-qualified_ node paths are unique. I have improved my answer to incorporate that rule. For anyone interested, at this point, the [repo](https://github.com/IVSoftware/sort_specific_node.git) **master** branch _is_ optimized, and there is also a `pre-optimized` branch that demonstrates iterating the tree to obtain a match. – IVSoftware Aug 03 '22 at 17:33
  • I guess I should upvote the question since it gave us such a lively discussion! – IVSoftware Aug 03 '22 at 18:11
  • 1
    Having a discussion with you is a privilege. Enjoy _problems solving_. There always will be different ways and approaches. _Perfection_ is a relative characteristic. Well done and nice efforts. – dr.null Aug 04 '22 at 01:44
  • 1
    Thank you, my friend. The respect is mutual. See you next time. – IVSoftware Aug 04 '22 at 11:25
0

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.

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)
    {
        List<TreeNode> tmp;
        TreeView tv = self.TreeView;

        if (order == SortOrder.Descending)
            tmp = self.Nodes.Cast<TreeNode>().OrderByDescending(n => n.Text).ToList();
        else
            tmp = self.Nodes.Cast<TreeNode>().OrderBy(n => n.Text).ToList();

        var sorter = tv.TreeViewNodeSorter as TreeNodeComparer ?? new TreeNodeComparer();
        sorter.SortOrder = SortOrder.None;

        tv.TreeViewNodeSorter = sorter;
        tv.BeginUpdate();
        self.Nodes.Clear();
        self.Nodes.AddRange(tmp.ToArray());
        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

dr.null
  • 4,032
  • 3
  • 9
  • 12
  • "Any attempts afterwards to sort the nodes in a different way fail??". Not if you set `treeView.Sorted = false;` – IVSoftware Aug 03 '22 at 11:45
  • @IVSoftware So what happens when you set it to `false`? Do we revert the `.Sort()` call by doing so? Why the property is hidden by design? Please refer to the source code and try to do what you are suggesting in the same context to examine the result. – dr.null Aug 03 '22 at 12:06
  • Calling `Sort` will set it to `true`. But why would you call `Sort` on a the entire tree to begin with when all you want is to sort a specific node? Referring to the source code, what it seems to do is create a problem that didn't exist, and then solves the problem it created. – IVSoftware Aug 03 '22 at 12:10
  • 1
    @IVSoftware Yes you're absolutely right. Hence the suggested solution here to adapt the built-in functionalities to get what we need and avoid the problems that you referred to. Good catch though. Also, sorting the entire tree still a requirement. We can't just throw a good feature. We are supposed to extend and add more. – dr.null Aug 03 '22 at 12:20