0

I have a list of objects with properties. The object class shown below:

public class ElementImpression
{
    public int ElementId { get; private set; }
    public string FamilyAndTypeName { get; private set; }
    public string CategoryName { get; private set; }
    public int CategoryNumber { get; private set; }
    public string SystemAbbreviation { get; private set; }
    public ElementImpression(Element e)
    {
        ElementId = e.Id.IntegerValue;
        FamilyAndTypeName = e.get_Parameter(BuiltInParameter.ELEM_FAMILY_AND_TYPE_PARAM).AsValueString();
        CategoryName = e.Category.Name;
        CategoryNumber = e.Category.Id.IntegerValue;
        SystemAbbreviation = e.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM).AsString();
    }
}

The goal is to parse the list and create a structured, hierarchical presentation in a TreeView control. The number of levels in the hierarchy and which properties to use as nodes is defined at runtime by the user. I have been succesful in creating the following treeview: Treeview by using the following code:

private void UpdateTreeView(object sender, MyEventArgs e)
    {
        //Level 0: All
        //Level 1: System Abbreviation
        //Level 2: Category Name
        //Level 3: Family and Type Name

        treeView1.BeginUpdate();
        treeView1.Nodes.Clear();
        
        //Payload is a container object holding the list to be parsed. It is cached as a property in the form.
        //Payload.ElementsInSelection is the list of objects to parse.
        var lv1Group = Payload.ElementsInSelection.GroupBy(x => x.SystemAbbreviation);
        treeView1.Nodes.Add("All");

        int i = -1;
        foreach (IGrouping<string, ElementImpression> group1 in lv1Group)
        {
            treeView1.Nodes[0].Nodes.Add(group1.Key);

            var lv2Group = group1.ToList().GroupBy(x => x.CategoryName);

            i++;
            int j = -1;
            foreach (IGrouping<string,ElementImpression> group2 in lv2Group)
            {
                treeView1.Nodes[0].Nodes[i].Nodes.Add(group2.Key);

                var lv3Group = group2.ToList();

                j++;
                int k = -1;
                foreach (ElementImpression ei in lv3Group)
                {
                    k++;
                    treeView1.Nodes[0].Nodes[i].Nodes[j].Nodes.Add(ei.FamilyAndTypeName);

                    treeView1.Nodes[0].Nodes[i].Nodes[j].Nodes[k].Nodes.Add(ei.ElementId.ToString());
                    treeView1.Nodes[0].Nodes[i].Nodes[j].Nodes[k].Nodes.Add(ei.CategoryNumber.ToString());
                }
            }
        }
        treeView1.EndUpdate();
    }

Is it possible to rewrite the UpdateTreeView() method, so that it accepts some kind of object, which tells the method how many levels and what properties to use, and then parses the data and creates the treeview dynamically at runtime? Is it possible to do it using recursion?

  • 1
    This is a pretty broad question, but yes, both are possible. Please show the code you've attempted so we can see what's not working, and narrow the question to the specific problem you're having. – Rufus L Nov 09 '20 at 21:54
  • Hi. Thank you for taking the time to answer. Well, I cannot progress further and the code shown in the last part of the question is as far as I can get. – Michail Golubjev Nov 10 '20 at 08:28

1 Answers1

0

Well, I managed to arrive at a solution. It is not recursive, but it is dynamic. Properties can be added or removed at runtime and the following code should parse it and populate the tree.

The general idea is to loop through all the objects which need to be organized to determine the FullPath of the deepest level node. Because I am using property values as nodes, each object already holds its' FullPath subparts. They just need to be combined in the correct order.

Then, starting from the root node at the top level, which is added manually, walk every step of the path checking if the node exists and if not creating it. This method successfully renders my data as needed in a treeview.

The FindTreeNodeByFullPath() method is from here.

public void PopulateTreeview()
    {
        //Manually add root node
        treeView1.Nodes.Add("All");
        
        //Loop all the objects
        foreach (ElementImpression e in Elements)
        {
            //Declare array to hold the names of all nodes in path to the element
            //PropertiesList is an object containing information about what properties to consider and how many.
            string[] pathParts = new string[PropertiesList.Length + 2];

            //The name of root node
            pathParts[0] = "All";

            //Populate the path parts with values from elements
            for (int i = 0; i < PropertiesList.Length; i++)
            {
                pathParts[i + 1] = PropertiesList[i].getPropertyValue(e);
            }
            //Finish the list with the name of the element (id currently)
            pathParts[pathParts.Length - 1] = e.Id.IntegerValue.ToString();

            //Create an array of all full paths from root node to the element
            string[] fullPaths = new string[PropertiesList.Length + 2];
            for (int i = 0; i < fullPaths.Length; i++)
            {
                if (i == 0) fullPaths[i] = pathParts[i];
                else fullPaths[i] = fullPaths[i - 1] + "." + pathParts[i];
            }

            //Iterate through the fullPaths to determine, if node exists, if not -> create it
            TreeNode previousNode = null;
            for (int i = 0; i < fullPaths.Length; i++)
            {
                TreeNode foundNode = treeView1.Nodes.FindTreeNodeByFullPath(fullPaths[i]);
                if (foundNode == null)
                {
                    if (previousNode != null) previousNode = previousNode.Nodes.Add(pathParts[i]);
                }
                else
                {
                    previousNode = foundNode;
                    continue;
                }
            }
        }
    }