0

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?

Capn Jack
  • 1,161
  • 9
  • 27
  • You can assign an instance of any arbitrary class (or any other value) to a TreeViewItem's `Tag` property. It's there for this purpose. I'm not going to bother writing a complete useful example for you because I don't have the time. If you provide any code, I'll modify that to show how it's done. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 16:14
  • @EdPlunkett See anything wrong with my attempt above? I posted it under my original question. – Capn Jack Oct 07 '16 at 16:22
  • Your CarProperties class has four *fields*, not four *properties*. See [What is the difference between a Field and a Property in C#?](http://stackoverflow.com/q/295104/719186) – LarsTech Oct 07 '16 at 18:01

1 Answers1

2

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" };
}
Community
  • 1
  • 1
  • Is the list stuff 100% necessary? it's confusing me a decent amount but I think I'm getting it a bit? Can you explain what you're doing when you add the " : TreeNode" to the end of your class declaration for CarTreeNode?Also what's the treeView1_AfterSelect for? Thanks for your patience – Capn Jack Oct 07 '16 at 18:11
  • @CapnJack I don't care how you store your CarProperties. Don't use a list if you don't like lists. `: TreeNode` means `CarTreeNode` inherits from `TreeNode`; we call it a "subclass of `TreeNode`". That means it has everything a `TreeNode` has, *plus* any additional methods and properties that it defines for itself. But you can always treat it as a plain old `TreeNode`, which is what the `TreeView` does. The `TreeView` doesn't even know it's not dealing with regular `TreeNode`s, which is the beauty of it. This is called "inheritance": `CarTreeNode` *inherits from* `TreeNode`. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:15
  • @CapnJack `treeView1_AfterSelect` is an "event handler". Double click on the `TreeView` in the form designer and by default, it will create an `AfterSelect` handler for you just like mine. That handler method is called every time the user clicks the mouse on a tree node. I used it to show you how to get the `CarProperties` out of the `TreeNode`. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:17
  • okay it's making a lot more sense now. Thanks for the explanation about inheritance. Always wondered what that was. – Capn Jack Oct 07 '16 at 18:18
  • @CapnJack I think it would be a good idea to run through a C# tutorial. You're missing some very, very basic knowledge of the language. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:18
  • have a good one in mind? I did this one: http://www.blackwasp.co.uk/CSharpFundamentals.aspx and couldn't find anything on code academy. – Capn Jack Oct 07 '16 at 18:19
  • @CapnJack Remember, the answer provides two totally separate and complete answers to the question -- I just updated the answer to make that more clear. Don't worry about the second one if all the inheritance stuff is blowing your mind right now. In programming, it's great to learn new stuff, but you can only learn one new concept at a time. In a given piece of code, there should be only one thing you don't fully understand yet. Nobody can learn anything from trying to use two unknowns at once. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:24
  • @CapnJack Unfortunately I don't have any suggestions. I learned C# ten years ago by trial and error and Google. I already had ten years as a C++ pro under my belt so I had all the fundamental concepts cold, and C# was a simpler language back then. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:26
  • @CapnJack My girlfriend has a very high opinion of Lynda.com for web and DB stuff. I can't vouch for them myself, and unfortunately it's a pay site. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:27
  • Yeah I'm currently on coop and learning through trial and error. That's the best way I find because you only learn what you use. I'm just going to stick to projects and assignments with help from friends I think. tutorials waste a lot of time on common things. Back to the question though can we talk about this part of the code?: "var car = e.Node.Tag as CarProperties;" Is var just a typless variable? This is the part I needed so I want to make sure I'm understanding 100% what this does. – Capn Jack Oct 07 '16 at 18:31
  • @CapnJack `var` is not typeless. It tells the compiler to figure out what the type should be from the rest of the code, and use that. So that statement is identical to `CarProperties car = e.Node.Tag as CarProperties;` -- but it saves you a little typing. And if you decide to cast `e.Node.Tag` to something else next week, you can do that without having to retype the typename at the start of the statement. There are times when the compiler can't figure out the type from context: `var foo;` will not compile. `var foo = 0;` compiles as `int foo = 0;`. `var` is mostly just to save keystrokes. – 15ee8f99-57ff-4f92-890c-b56153 Oct 07 '16 at 18:34