Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Quick and Dirty TreeView

, 25 Mar 2013
Rate this:
Please Sign up or sign in to vote.
Design to create a simple tree view that can be copy and pasted

Introduction

There are times when it would be convenient to have an easy way to display hierarchical data as tree. This can be done using tree view, but sometimes just a text version is acceptable, with the advantages of being easy to handle the data, not having to carry a lot of objects and values around, and for copy and pasting. It is not a hard problem to solve; it just needs to be thought about a little while.

Implementation

The approach used is a recursion, with a List<string> being used to capture a tree. When assembling the tree, each subtree is created as a List<string> and combined into a List<List<string>>. The first element in the List<string> is the node that needs be attached to the base node of the tree. When assembling the nodes of a node in the tree, the first List<string> in the List<List<string>> is the parent, and the children are represented by the following nodes. The following code will put a horizontal line made up of the “|” character from the parent for each node, ending at the start of the last node. Where the base node starts for each child node, a “+” character with a vertical line right (“-“) will be inserted:

private static List<string> AppendTree(List<list<string>> limbs)
{
  var list = new List<string>();
  
  if (limbs.Any())
  {
    list.Add(limbs.First().First());
    for (int i = 1; i < limbs.First().Count; i++)
      list.Add("  |     " + limbs.First()[i]);
  }
  
  for (int i = 1; i < limbs.Count - 1; i++)
  {
    if (limbs[i].Count > 0)
    {
      list.Add(" +- " + limbs[i][0]);
      for (int j = 1; j < limbs[i].Count; j++)
        list.Add("  |     " + limbs[i][j]);
    }
  }
  
  if (limbs.Last().Count > 1)
  {
    list.Add(" +- " + limbs.Last()[0]);
    for (int j = 1; j < limbs.Last().Count; j++)
      list.Add("        " + limbs.Last()[j]);
  }
  
  return list;
}

What is finally returned is a List<string>.

I also have several helper methods, one will take a list of arguments and put them into a comma delimited string enclosed in brackets, and add a spacer line:

private static List<string> Enclose(params object[] o)
{
  var s = o.Select(i => i.ToString());
  var str = string.Join(", ", s);
  return new List<string> { string.Format("< {0} >", str), string.Empty };
}

A second method will take a string and an IEnumerable<T> and convert it so something similar, except this allows quick conversion of list data to a single line:

private static List<string> EncloseList<t>(string s, IEnumerable<t> o)
{
  return Enclose(s, string.Join(", ", o.Select(i => i.ToString())));
}

A fourth method is just a simple factory for creating a List<List<string>> from a single List<string> which becomes the first element of the new list. It just cleans up creating the conversion methods:

private static List<list<string>> StartList(List<string> list)
{
  return new List<list<string>> { list };
}

One final helper method is used after the objects to displayed in the tree are process. It converts the List<string> to a single string with carriage returns between the strings in the List<string>:

public static string Convert(IEnumerable<string> values)
{
  var sb = new StringBuilder();
  foreach (var value in values)
  {
    sb.AppendLine("    " + value);
  }
  return sb.ToString();
}

To make life simple, the method Convert is overloaded. Each Convert method takes a single argument and returns a List<string>. The simplest convert would just return the result of a call to the Enclose method:

public static List<string> Convert(Class4 values)
{
  return Enclose("Class 4", values.Id, values.Name, values.Property1);
}

To create a class to display a class and its children as a limb:

public static List<string> Convert(Class1 values)
{
  var result = StartList(Enclose("Class 1", values.Id, values.Name, 
          values.Property1));
  if (values.Children != null)
    result.AddRange(values.Children.Select(Convert));
  return AppendTree(result);
}

Once all the methods are set up to display a tree for an object and its children, two method calls are required, and it is the same for all classes for which Convert methods have been created:

var str = ConvertToTreeViewString.Convert(
            ConvertToTreeViewString.Convert(data)); 

The result would look something like this:

    < Class 1, 1, name1, property1a >
     |     
     +- < Class 2, 1, name2a, property2a >
     |      |     
     |      +- < Property4, a, b, c, d >
     |      |     
     |      +- < Class 2, 2, name3a, property3a >
     |      |      |     
     |      |      +- < Property4, 1, 2, 3, 4 >
     |      |             
     |      +- < Class 2, 3, name3b, property3d >
     |              |     
     |              +- < Property4, 5, 6, 7, 8 >
     |              |     
     |              +- < Class 4, 4, name4a, property4a >
     |              |     
     |              +- < Class 4, 5, name4b, property4d >
     |                     
     +- < Class 2, 6, name2b, property2d >
            |     
            +- < Property4, a, b, c, d >
            |     
            +- < Class 2, 6, name3c, property3g >
            |       |     
            |      +- < Property4, 7, 2, 3, 4 >
            |             
            +- < Class 2, 1, name3d, property3j >
                  |     
                  +- < Property4, 8, 2, 3, 4 >

If you have control of your objects, you can simplify things by having the object information to be displayed in the tree returned in the ToString() method. I did not have that luxury since the objects in the tree were generated objects. Also I did not have a standard name for the children of an object for the same reason. I made a little bit of use of generics in my code when children were displayed as list, but that was it. A different application may allow use of generics to reduce the number of convert methods required.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Clifford Nelson
Software Developer (Senior) ETeam/Delloitte
United States United States
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last three years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with NBC Universal in Universal City, CA

Comments and Discussions

 
GeneralMy vote of 1 Pinmembersupernorb26-Mar-13 2:57 
AnswerRe: My vote of 1 PinmemberClifford Nelson26-Mar-13 5:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140922.1 | Last Updated 26 Mar 2013
Article Copyright 2013 by Clifford Nelson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid