Capn Jack Capn Jack - 1 month ago 14
C# Question

Assign custom properties to a node in TreeView windows forms?

Say I have a TreeView of cars with different manufacturers, then a sub tree of models, etc. If I want each node to have a set of properties how would I do that? Would I make a new class and then assign each node to a class in some way? I'm having difficulty conceptualizing this but I imagine it is possible. What would be the point of a TreeView if you couldn't add data to each member?

In my right click menu for the carModelNode I have an option called properties. When the user clicks it it opens a form where the user then enters/edits data such as the year of the car, colour, manual/auto, etc. How can I then store that data and associate it with that node? Is there an easy way to do this or is this going to call for more of a jerry rigged approach?

**Please provide some examples with what you're talking about because I'm still not very good with syntax!

EDIT: my attempt below is for @Ed Plunkett

A class with the properties I want each node to have:

public class CarProperties
{
public string type = "";
public string name = "";
public int year = 0;
public bool isManual = false;
}


And now trying to assign these properties to a node:

CarProperties FordFocus = new CarProperties();
FordFocus.name = "exampleName";
...
treeIO.SelectedNode.Tag = FordFocus;


Does this look about right?

Answer

Two ways to do this: The simplest way is to use the Tag property of TreeNode.

public Form1()
{

    InitializeComponent();

    //  Horrible example code -- in real life you'd have a method for this
    foreach (var car in cars)
    {
        var tn = new TreeNode(car.name)
        {
            Tag = car
        };

        treeView1.Nodes.Add(tn);
    }
}

public List<CarProperties> cars = new List<CarProperties>()
{
    new CarProperties() { name = "Ford Focus" },
    new CarProperties() { name = "TVR Tamora" },
    new CarProperties() { name = "Toyota Tacoma" },
};

private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
    //  This is a "cast": IF e.Node.Tag is actually an instance of CarProperties,
    //  the "as" operator converts it to a reference of that type, we assign 
    //  that to the variable "car", and now you can use it's properties and methods. 
    var car = e.Node.Tag as CarProperties;

    MessageBox.Show("Selected car " + car.name);
}

So that's that. But here's another, more complicated, but more powerful way to do the same thing:

I found an answer with what's arguably a nicer way to do it: Subclass TreeNode, and you can add a strongly typed property and even other stuff. A setter for the Car property could easily update TreeNode.Text, so if you assign a new Car to an existing CarTreeNode, it updates the UI automagically.

public class CarTreeNode : TreeNode
{
    //  ...plus, clone all the other constructors
    public CarTreeNode(CarProperties car) :base(car.name) {
        Car = car;
    }

    private CarProperties _car;
    public CarProperties Car { 
        get { return _car; }
        set {
            _car = value;
            //  Let it throw an exception if value was null
            base.Text = Car.name;
        }
    }
}

... snip ...

//  Regrettably, TreeNodeCollection.AddRange() is an old-fashioned method 
//  that takes an array
treeView1.Nodes.AddRange(cars.Select(car => new CarTreeNode(car)).ToArray());

... snip ...

private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
    var ctn = e.Node as CarTreeNode;

    //  In real life there's no reason you'd ever want to replace any car 
    //  that the user clicks on, but it demonstrates the principle. 
    ctn.Car = new CarProperties() { name = "Aston Martin Vanquish" };
}
Comments