Dan W
Dan W

Reputation: 3628

Convert tree-like class data structure to Winforms TreeView for ease of debugging visually

Using C#, I have created a "node" class (which contains a List of type itself) to allow for recursive tree-like data structures. This is a simplified version of the class:

public class node
{
    public List<node> c = null;
    public int data = 0;

    public node(int data)
    {
        this.data = data;
    }
}

Unfortunately, I've come across a point in my program where it's getting very hard to debug due to the nature of trees. Large trees especially are difficult to probe visually.

For this reason, I had the brain wave to use Winform's TreeView control to display the data so I can more easily visualize what's going on. I understand and have implemented preorder and inorder traversal for a minimax algorithm, and so I was wondering if I could utilize some of that code to help with converting from my own node tree to a TreeView tree.

The method skeleton would look like this:

private void convertNodetreetoTreeview(node n)
{
    TreeNode root = new TreeNode("Root node");
    
    ...

    treeView1.Nodes.Add(root);
}

How would I go about this?

For speed, I would prefer an iterative solution, but a recursive solution would be fine too.

Upvotes: 1

Views: 807

Answers (3)

Reza Aghaei
Reza Aghaei

Reputation: 125197

To convert a custom node class to a TreeNode, you can write a simple recursive extension method.

Assuming you have a class like this:

public class Node
{
    public List<Node> Nodes { get; } = new List<Node>();
    public string Text { get; set; }
    public Node(string data)
    {
        this.Text = data;
    }
}

Then you can write a simple recursive extension method like the following:

public static class NodeExtensions
{
    public static TreeNode ToTreeNode(this Node node)
    {
        var treeNode = new TreeNode(node.Text);
        foreach (Node child in node.Nodes)
            treeNode.Nodes.Add(child.ToTreeNode());
        return treeNode;
    }
}

Later, to use it to populate a TreeView:

var node = new Node("1");
node.Nodes.Add(new Node("1-1"));
node.Nodes.Add(new Node("1-2"));
node.Nodes[0].Nodes.Add(new Node("1-1-1"));
node.Nodes[1].Nodes.Add(new Node("1-2-1"));
node.Nodes[1].Nodes.Add(new Node("1-2-2"));

treeView1.Nodes.Add(node.ToTreeNode());

To see a general solution to populate a TreeView based on a DataTable or any list which contains tree-like data, take a look at: Populate TreeView from DataTable or List that contains tree-like data

More information:

Upvotes: 0

Reza Aghaei
Reza Aghaei

Reputation: 125197

I take the following statement and as I mentioned in the comments, as an idea you can also Create a custom data visualizer to use in Visual Studio to visualize your data at debug-time.

for ease of debugging visually

Then, when you are debugging your application wherever you have a breakpoint for the node class, you will see the visualizer icon:

enter image description here

And if you click on it, you can see your custom visualizer, which could be a Form containing a TreeView:

enter image description here

Create a custom debugger visualizer for my tree-like data structure

There are a few nice articles and guides in Microsoft Docs which explains the architecture of the visualizers and teach you how to create an install a simple visualizer. Here I also put a very simplified guideline to show you how you can achieve what I showed in above screenshots:

  1. Create a .NET Framework Class Library for you data class. Name the project MyDataObjects. This project will contain your data objects which you want to create a custom visualizer for them. Here, Node class. Use the same node class in your main project as well.

  2. Add the following Node.cs class to it:

    using System;
    using System.Collections.Generic;
    
    namespace MyDataObjects
    {
        [Serializable]
        public class Node
        {
            private Node() { }
            public List<Node> Nodes { get; } = new List<Node>();
            public string Text { get; set; }
            public Node(string data)
            {
                this.Text = data;
            }
        }
    }
    
  3. Create a .NET Framework Class Library for you visualizer. Name the project MyDataObjectsVisualizers.

  4. Add reference to Microsoft.VisualStudio.DebuggerVisualizers.dll which you can browse from:

  • <Visual Studio Install Directory>\Common7\IDE\PublicAssemblies
  1. Add reference to System.Windows.Forms.dll which you can find in framework assemblies.

  2. Add the following NodeExtensions.cs class to it:

    using MyDataObjects;
    using System.Windows.Forms;
    
    namespace MyDataObjectsVisualizers
    {
        public static class NodeExtensions
        {
            public static TreeNode ToTreeNode(this Node node)
            {
                var treeNode = new TreeNode(node.Text);
                foreach (Node child in node.Nodes)
                    treeNode.Nodes.Add(child.ToTreeNode());
                return treeNode;
            }
        }
    }
    
  3. Add a new form to the project and name it NodeVisualizerForm.cs. Drop a TreeView on the form and set its Dock to Fill. Then add the following code to the form:

    using MyDataObjects;
    using System;
    using System.Windows.Forms;
    
    namespace MyDataObjectsVisualizers
    {
        public partial class NodeVisualizerForm : Form
        {
            public NodeVisualizerForm()
            {
                InitializeComponent();
            }
            public Node Node { get; set; }
            protected override void OnLoad(EventArgs e)
            {
                base.OnLoad(e);
                if (Node != null)
                    treeView1.Nodes.Add(Node.ToTreeNode());
            }
        }
    }
    
  4. Add a DebuggerSide.cs class to the project with the following code:

    using Microsoft.VisualStudio.DebuggerVisualizers;
    
    [assembly: System.Diagnostics.DebuggerVisualizer(
        typeof(MyDataObjectsVisualizers.DebuggerSide),
        typeof(VisualizerObjectSource),
        Target = typeof(MyDataObjects.Node),
        Description = "Sample Visualizer")]
    
    namespace MyDataObjectsVisualizers
    {
        public class DebuggerSide : DialogDebuggerVisualizer
        {
            protected override void Show(IDialogVisualizerService windowService,
                IVisualizerObjectProvider objectProvider)
            {
                var node = objectProvider.GetObject() as MyDataObjects.Node;
                var form = new NodeVisualizerForm();
                form.Node = node;
                windowService.ShowDialog(form);
            }
        }
    }
    
  5. Build the solution, and copy the following files to the following locations:

  • Copy MyDataObjectsVisualizers.dll to:
    [VS Installation Folder]\Common7\Packages\Debugger\Visualizers
  • Copy MyDataObjects.dll to:
    [VS Installation Folder]\Common7\Packages\Debugger\Visualizers\netstandard2.0
  1. Then close all visual studio instances. Then open the solution which you ant to debug and set a breakpoint for node, and run the application. There you go! You can see the visualizer icon (the magnifier) close to the node, like what I showed in the screenshot above and by clicking on it, you will see the visualizer form.

Please take note, in the main project, you should use the same Node dll that we created in MyDataObjects project.

More information:

Upvotes: 1

Dan W
Dan W

Reputation: 3628

Below is the code to go about just what you're asking for. Instead of utilizing a Stack and a List (as you might for usual preorder traversal), you utilize TWO stacks - one for the node tree you have, and one for the TreeView tree. From there, it's a simple case of pushing TreeView nodes when custom nodes are being pushed, and popping TreeView nodes when your custom nodes are being popped. Whilst you're pushing TreeView nodes, you simultaneously add these nodes to the TreeView using the top-most TreeView stack reference.

Feel free to replace the mystringdata variable with one of your choice.

private void convertNodetreeToTreeview(node n)
{
    Stack<node> stack1 = new Stack<node>();
    Stack<TreeNode> stack2 = new Stack<TreeNode>();                     

    stack1.Push(n);
    TreeNode root = new TreeNode( n.mystringdata );
    stack2.Push(root);

    while (stack1.Count > 0)
    {
        node t = stack1.Pop();
        TreeNode r = stack2.Pop();
        for (int i = 0; i < t.c.Count; i++)
        {
            if (t.c[i] != null)
            {
                stack1.Push(t.c[i]);
                TreeNode rchild = new TreeNode(t.c[i].mystringdata );
                r.Nodes.Add(rchild);
                stack2.Push(rchild);
            }
        }               
    }
    treeView1.Nodes.Add(root);
}

Upvotes: 0

Related Questions