Strange issue with my TreeView and its treenodes where the child nodes aren't "found"..

Saturn

Member
Joined
Jul 30, 2023
Messages
8
Programming Experience
1-3
I will try my best to explain this. I'm working on an app for Papyrus(namely Starfield's version of it), and my TreeView displays all my parent and child nodes correctly.

For example(indent wont work like I want for the lower part):
Actor Script
* Functions​
* Events​
ActorBase Script
* Functions
* Events

I have 47 parent nodes, and that is the amount that treeView.Nodes.Count reports. I don't know if that's supposed to return all nodes, but whatever. I am trying to get around having to hardcode every single node just to add new nodes. This is my code for creating the 47 parent nodes:

C#:
        public static void CreateParentNodes(TreeView treeView)
        {
            for (int i = 0; i < ScriptObjects.Count; i++)
            {
                treeView.Nodes.Add(ScriptObjects[i], ScriptObjects[i]);
            }
        }


Now I need to add the nodes for the categories "Functions" and "Events" as children to each of the parent nodes:


C#:
        private static void AddCategoriesToParentNodes(TreeView treeView)
        {
            for(int i = 0; i < treeView.Nodes.Count; i++)
            {
                for (int j = 0; j < Categories.Count; j++)
                {
                    treeView.Nodes[i].Nodes.Add(Categories[j], Categories[j]);
                }
            }
        }

Here is where the strange thing begins. When I need to add a new node to a child node of a parent node, I get the classic ArgumentOutOfRange exception. Im thinking maybe Im just getting the structure wrong, treeviews can be confusing sometimes, so I did a little test with the first parent node: treeView.Node[0].Nodes.Count this returned 0, despite there obviously being 2 children of this node. Why is happening?
 
Seems to work for me:

1720358721257.png


C#:
using System;
using System.Windows.Forms;
using System.Collections.Generic;

namespace WinForms
{
    class MainForm : Form
    {
        List<string> _scriptObjects = new List<string>();

        List<string> _categories = new List<string>()
        {
            "Functions",
            "Events",
        };

        MainForm()
        {
            var treeView = new TreeView()
            {
                Dock = DockStyle.Fill,
                AutoSize = true
            };

            var btnAddScriptObjects = new Button()
            {
                Text = "Add Script Objects",
                AutoSize = true
            };
            btnAddScriptObjects.Click += (o, e) => AddScriptObjects(treeView);

            var btnCategories = new Button()
            {
                Text = "Add Categories",
                AutoSize = true
            };
            btnCategories.Click += (o, e) => AddCategories(treeView);

            var layout = new FlowLayoutPanel()
            {
                Dock = DockStyle.Bottom,
                AutoSize = true,
            };

            Controls.Add(treeView);
            Controls.Add(layout);
            layout.Controls.Add(btnAddScriptObjects);
            layout.Controls.Add(btnCategories);

            for (int i = 0; i < 47; i++)
                _scriptObjects.Add($"Script Object {i + 1}");
        }

        void AddScriptObjects(TreeView treeView)
        {
            for(int i = 0; i < _scriptObjects.Count; i++)
                treeView.Nodes.Add(_scriptObjects[i], _scriptObjects[i]);
        }

        void AddCategories(TreeView treeView)
        {
            for (int i = 0; i < treeView.Nodes.Count; i++)
            {
                for (int j = 0; j < _categories.Count; j++)
                    treeView.Nodes[i].Nodes.Add(_categories[j], _categories[j]);
            }
        }

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}
 
Last edited:
Yeah like I said, they'll get added to the parent nodes(and I can add more child nodes), it's when I try to add new nodes(grandchildren?) to those child nodes is where I get the exception.

To better explain it.. I can't do this:

Functions
* AddInventoryEventFilter
Events
* OnItemAdded

I think I'll have to erase it all and start with a different approach.
 
Last edited:
That's not what you said. You said the problem you were encountering was adding children (categories) to the parent nodes. To quote from your original post #1:
Now I need to add the nodes for the categories "Functions" and "Events" as children to each of the parent nodes:
:
Here is where the strange thing begins. When I need to add a new node to a child node of a parent node, I get the classic ArgumentOutOfRange exception.

It's only in your post #3 where you say that the problem is trying to add children to the child nodes.

Additionally, all you tell us is that you are getting an argument out of range exception, but don't bother to show us the code where you are trying to add to add grand children. We are not clairvoyant. We cannot see the code that you have written.
 
Anyway, it still works for me:
1720417448912.png


C#:
using System;
using System.Windows.Forms;
using System.Collections.Generic;

namespace WinForms
{
    class MainForm : Form
    {
        List<string> _scriptObjects = new List<string>();

        List<string> _categories = new List<string>()
        {
            "Functions",
            "Events",
        };

        List<string> _grandChilden = new List<string>()
        {
            "AddInventoryEventFilter",
            "OnItemAdded",
        };

        MainForm()
        {
            var treeView = new TreeView()
            {
                Dock = DockStyle.Fill,
                AutoSize = true
            };

            var btnAddScriptObjects = new Button()
            {
                Text = "Add Script Objects",
                AutoSize = true
            };
            btnAddScriptObjects.Click += (o, e) => AddScriptObjects(treeView);

            var btnCategories = new Button()
            {
                Text = "Add Categories",
                AutoSize = true
            };
            btnCategories.Click += (o, e) => AddCategories(treeView);

            var btnGrandChildren = new Button()
            {
                Text = "Add Grandchildren",
                AutoSize = true
            };
            btnGrandChildren.Click += (o, e) => AddGrandChildren(treeView);

            var layout = new FlowLayoutPanel()
            {
                Dock = DockStyle.Bottom,
                AutoSize = true,
            };

            Controls.Add(treeView);
            Controls.Add(layout);
            layout.Controls.Add(btnAddScriptObjects);
            layout.Controls.Add(btnCategories);
            layout.Controls.Add(btnGrandChildren);

            for (int i = 0; i < 47; i++)
                _scriptObjects.Add($"Script Object {i + 1}");
        }

        void AddScriptObjects(TreeView treeView)
        {
            for(int i = 0; i < _scriptObjects.Count; i++)
                treeView.Nodes.Add(_scriptObjects[i], _scriptObjects[i]);
        }

        void AddCategories(TreeView treeView)
        {
            for (int i = 0; i < treeView.Nodes.Count; i++)
            {
                for (int j = 0; j < _categories.Count; j++)
                    treeView.Nodes[i].Nodes.Add(_categories[j], _categories[j]);
            }
        }

        void AddGrandChildren(TreeView treeView)
        {
            for (int i = 0; i < treeView.Nodes.Count; i++)
            {
                var parent = treeView.Nodes[i];
                for (int j = 0; j < parent.Nodes.Count; j++)
                {
                    var child = parent.Nodes[j];
                    var grandChildString = _grandChilden[j % _grandChilden.Count];
                    child.Nodes.Add(grandChildString);
                }
            }
        }

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}
 
That's not what you said. You said the problem you were encountering was adding children (categories) to the parent nodes. To quote from your original post #1:

:


It's only in your post #3 where you say that the problem is trying to add children to the child nodes.

Additionally, all you tell us is that you are getting an argument out of range exception, but don't bother to show us the code where you are trying to add to add grand children. We are not clairvoyant. We cannot see the code that you have written.

Sounds like you took it a bit personal there and I'm sorry for that. It is likely due to my degraded fluency in my first language which is English. I don't use it nearly as much as I use to in both writing and speaking, reading however remains high. I find I am struggling to explain things as well I used to and it can appear out of order or the context is just in disarray.

I didn't post the all code as I didnt think some of it was relevant, although I do wish I had posted the Call Stack. This is something I will be sure to do from now on.

The code you posted was much appreciated and you use concepts that I will later adapt to my style. FlowLayoutPanel is a control I dont quite understand the inner workings yet.

I did solve the matter with a different approach using a nested list. The whole code:

C#:
namespace SFPapyrusReference
{
    internal static class PapyrusTree
    {
        private static List<string> _scriptObjects = new List<string>
        {
            "ActorBase Script", "Actor Script", "ActiveMagicEffect Script", "Activator Script", "Alias Script",
            "Cell Script", "CommonArrayFunctions Script", "EffectShader Script", "EncounterZone Script", "Enchantment Script",
            "Faction Script", "Form Script", "FormList Script", "Furniture Script", "Game Script",
            "GlobalVariable Script", "ImageSpaceModifier Script", "Ingredient Script", "Instance Naming Rules Script", "Keyword Script",
            "LeveledActor Script", "LeveledItem Script", "LeveledSpell Script", "Location Script", "LocationAlias Script", "ObjectReference Script",
            "MagicEffect Script", "Message Script", "MiscObject Script", "Package Script", "Perk Script",
            "Potion Script", "Quest Script", "Scene Script", "ScriptObject Script", "Scroll Script", "ShaderParticleGeometry Script",
            "Sound Script", "SoundCategory Script", "Spell Script", "Terminal Script", "Topic Script",
            "TopicInfo Script", "Utility Script", "VisualEffect Script", "Weapon Script", "Weather Script"
        };

        private static List<string> _categories = new List<string>
        {
            "Functions", "Events"
        };

        public static void CreatePapyrusTreeView(Form akForm)
        {
            TreeView treeView = new TreeView
            {
                Name = "Papyrus Tree View",
                FullRowSelect = false,
                Scrollable = true,
                Width = 400,
                Height = 870,
                Location = new Point(5, 15),
                ShowRootLines = true,
                ShowPlusMinus = true,
                TabIndex = 0,
            };
            akForm.Controls.Add(treeView);

            CreateParentNodes(treeView);
        }

        public static void CreateParentNodes(TreeView treeView)
        {

            treeView.BeginUpdate();

            for (int i = 0; i < _scriptObjects.Count; i++)
            {
                treeView.Nodes.Add(_scriptObjects[i], _scriptObjects[i]);
            }

            AddCategoriesToParentNodes(treeView);
            AddFunctionsFromScripts(treeView);

            treeView.EndUpdate();
        }

        private static void AddCategoriesToParentNodes(TreeView treeView)
        {
            for(int i = 0; i < treeView.Nodes.Count; i++)
            {
                for (int j = 0; j < _categories.Count; j++)
                {
                    treeView.Nodes[i].Nodes.Add(_categories[j], _categories[j]);
                }
            }
        }

        private static void AddFunctionsFromScripts(TreeView treeView)
        {
            // A nested list containing 47 lists.
            for(int i = 0; i < ScriptLists.ListOfFunctions.Count; i++)
            {
                // The first level index corresponds to the index of the parent node.
                for (int j = 0; j < ScriptLists.ListOfFunctions[i].Count; j++)
                {
                    // For each parent node that has a corresponding function list, add that list as a grandchild.
                    treeView.Nodes[i].Nodes[j].Nodes.Add(ScriptLists.ListOfFunctions[i][j]);
                }
            }
        }
    }
}

; Nested List
C#:
namespace SFPapyrusReference
{
    internal static class ScriptLists
    {

        public static List<List<string>> ListOfFunctions = new List<List<string>>()
        {
            new List<string>
            {
                "Test"
            },

            new List<string>
            {
                "Test 2"
            }
        };

    }
}

Form main;
C#:
namespace SFPapyrusReference
{
    internal class FormMain : Form
    {
        public FormMain()
        {
            ClientSize = new Size(1200, 900);
            PapyrusTree.CreatePapyrusTreeView(this);

        }
    }
}

This is all going to be dynamically populated when the form loads. The user will be able to select items from the functions / events and in the (not coded yet) "window" on the right will display information about the selected item.

Current result:

Screenshot 2024-07-08 120709.png
Screenshot 2024-07-08 120709.png
 
I havent the foggiest idea why the image is displaying twice and why the bigger one XD. Anyway, thank you for taking the time to reply.

Edit: I was incorrect with my approach above and am slightly embarrassed. I will apply your concepts and be done with it.
 
Last edited:
Back
Top Bottom