How to create Array[T] without specifying length

I want to create a method that given a list of strings creates nodes in a TreeView.

Apparently TreeNode could be an Array -> TreeNode[]

But I can’t figure out how to define an empty Array[TreeNode]() that I can add TreeNodes to. I tried everything I found on internet:

  • Array.__new__(TreeNode,0)
  • Array.CreateInstance(TreeNode,0)
  • List, ArrayList, simple [] just don’t work.

Could anyone shed some light how can I define an empty TreeNode array without fixing the size of it?

One solution I saw but didn’t try was deriving a class from System.Collections.IList creating my own list. I hope I don’t have to do that for such a stupid thing.

Thanks in advance.

Update:
This is my crazy-ass workaround. I don’t like it.

import clr

clr.AddReference("mscorlib")
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import TreeView, TreeNode, DockStyle
from System.Drawing import Size


from System import Array


class IForm(Form):

    def __init__(self):
        self.Text = 'TreeView'
        tv = TreeView()
        root = TreeNode()
        root.Text = 'Languages'
        root_sub_list = ["CPython2","CPython3","IronPython","Jython","Cython","PyPy"]
        root.Nodes.AddRange(self.add_list_node(root,root_sub_list))
        
        tv.Parent = self
        tv.Nodes.Add(root)
        
        tv.Dock = DockStyle.Fill
        tv.AfterSelect += self.AfterSelect
        
        self.sb = StatusBar()
        self.sb.Parent = self
        
        self.Size = Size(220, 220)
        self.CenterToScreen()
        
        
    def AfterSelect(self, sender, event):
        """ Name of the selected node will be displayed in the statusbar """
        self.sb.Text = event.Node.Text
    
    def add_list_node(self,subroot,node_list):
        if isinstance(subroot,TreeNode) == False: return
        
        node_array = Array.CreateInstance(TreeNode, 100)
        for i in range(len(node_list)):
            child_node = TreeNode()
            child_node.Text = node_list[i]
            node_array.SetValue(child_node,i)
        node_array = node_array[:len(node_list)]
        return node_array


if __name__=="__main__":
    
    standalone = True # False if script is run inside Rhino
    if standalone:
        Application.Run(IForm())
    else:
        form = IForm()
        form.Show()

Hi @ivelin.peychev,

first of all, you can’t create Arrays with variable lenghts. Arrays have a fixed length and lists have a variable length

What you can do though: you can add treenodes to treenodes, so no need for that fancy array creation. Maybe something like this:

tv = TreeView()
root = TreeNode("Languages")
root_sub_list = ["CPython2","CPython3","IronPython","Jython","Cython","PyPy"]

for sub in root_sub_list:
    tv.Nodes[0].Nodes.Add(TreeNode(sub))

Not tested, but something like this should work

Edit: Of course i would advise against doing Winforms in Python, use a proper IDE instead, to avoid such errors, but i know your take on this topic :wink:

1 Like

Thanks @lando.schumpich,

I’ll test this out.

Lately I started using SharpDevelop to create the forms, but what I’m trying is dynamically created tree, out of a json file.

I needed to figure out a way before jumping into creating the other parts of the form.

This likey won’t work so maybe update with your json code then, but for your case it should be easier to iterate over normal python lists and use the Add Method of the treenodes created

It doesn’t work because it doesn’t consider a TreeNode an array, unless you create an array in advance and then use .AddRange().

It is infuriating.

Adding single objects is fine, but if you have a list coming from the json, you end up banging your head on a wall.

tv.Nodes[0].Nodes.Add(TreeNode(sub))

doesn’t work in python?

Testing… :slight_smile:

I want it in a method so I can use it recursively :wink:

why the [0] after Nodes?

I see the sample code was unfinished:

# create variables
tv = TreeView()
root = TreeNode("Languages")
root_sub_list = ["CPython2","CPython3","IronPython","Jython","Cython","PyPy"]

# Add node to tv
tv.Nodes.Add(root)

# add sub nodes to first node in treeview
for sub in root_sub_list:
    tv.Nodes[0].Nodes.Add(TreeNode(sub))

tv.Nodes gives you back a collection of all top-level nodes in your treeview. You can then index that collection and add child nodes to the nodes you get bakc by doing so.

import clr

clr.AddReference("mscorlib")
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, StatusBar
from System.Windows.Forms import TreeView, TreeNode, DockStyle
from System.Drawing import Size


from System import Array


class IForm(Form):

    def __init__(self):
        self.Text = 'TreeView'
        tv = TreeView()
        root = TreeNode()
        root.Text = 'Languages'
        root_sub_list = ["CPython2","CPython3","IronPython","Jython","Cython","PyPy"]
        ''' lando code'''
        for lingo in root_sub_list:
            root.Nodes.Add(TreeNode(lingo))
        ''' '''
        #root.Nodes.AddRange(self.add_list_node(root,root_sub_list))
        
        tv.Parent = self
        tv.Nodes.Add(root)
        
        tv.Dock = DockStyle.Fill
        tv.AfterSelect += self.AfterSelect
        
        self.sb = StatusBar()
        self.sb.Parent = self
        
        self.Size = Size(220, 220)
        self.CenterToScreen()
        
        
    def AfterSelect(self, sender, event):
        """ Name of the selected node will be displayed in the statusbar """
        self.sb.Text = event.Node.Text
    
    def add_list_node(self,subroot,node_list):
        if isinstance(subroot,TreeNode) == False: return
        
        node_array = Array.CreateInstance(TreeNode, 100)
        for i in range(len(node_list)):
            child_node = TreeNode()
            child_node.Text = node_list[i]
            node_array.SetValue(child_node,i)
        
        node_array = node_array[:len(node_list)]
        return node_array


if __name__=="__main__":
    
    standalone = True # False if script is run inside Rhino
    if standalone:
        Application.Run(IForm())
    else:
        form = IForm()
        form.Show()

A little different but…

Am I missing something? It doesn’t work.

Fixed it. removed the Nodes[0] because root is a root :smiley:

Hi @ivelin.peychev, you might just add to a list, then make the array from it later eg:

import System

from System.Collections.Generic import List
from System.Windows.Forms import TreeNode

def DoSomething():

    nodes_list = List[TreeNode]()
    nodes_list.Add(TreeNode("Hello"))
    nodes_list.Add(TreeNode("World"))

    nodes_array = System.Array[TreeNode](nodes_list)

DoSomething()

_
c.

1 Like

Thanks @clement,

This is it.

I remember once before I had to create a list before creating an IEnumerable, I guess I have to do the same with Arrays.