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

Generic TreeView

, 29 Jan 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Populate a TreeView from your object model

Introduction

This article describes how to populate a TreeView control in a generic fashion and with very little code. On the project that I'm writing I had a TreeView control that would populate itself from the object model used in the project. The problem that I had was that the custom TreeView control was tightly coupled via interfaces to the object model. I wanted a TreeView control that did not know about the object model that I was using so I could use it in different projects that had different object models.

Solution

The solution was to have a TreeView control use reflection to pick out information from the object model using attributes on the objects. The class ProjectItem shown below, has three properties that have the TreeNodeAttribute applied to them:

[Serializable]
public class ProjectItem : NamedObjectItem
{
  public ProjectItem(string sName)
    : base(sName)
  {
    Assemblies = new ContainerItem<AssemblyItem>("Assemblies");
    Backgrounds = new ContainerItem<IBackgroundItem>("Backgrounds");
    MaterialSetsItem = new ContainerItem<ContainerItem<MaterialItem>>("Material Sets");
  }
  [TreeNodeAttribute]
  public ContainerItem<AssemblyItem> Assemblies { get; set; }
  [TreeNodeAttribute]
  public ContainerItem<IBackgroundItem> Backgrounds { get; set; }
  [TreeNodeAttribute( Hide = true)]
  public ContainerItem<ContainerItem<MaterialItem>> MaterialSetsItem { get; set; }
}

The TreeNodeAttribute is a very simple class and is used to identify which properties should be retrieved from the object for the TreeView control:

  public class TreeNodeAttribute : Attribute
  {
  }

I derived an AttributeTreeView control from the standard TreeView control, and added three methods to process objects that should be added to the TreeView control. These methods are:

public void Populate<TAttribute>(object item, string sPropertyForText)
  where TAttribute : Attribute
public void Populate<TAttribute>(object item,
  TreeNode tnParent, string sPropertyForText)
  where TAttribute : Attribute
private TreeNode Add<TAttribute>(object item,
  TreeNode treeNodeParent, string sPropertyForText)
  where TAttribute : Attribute

As you can see from the methods, we need to supply some information for the AttributeTreeView control to use. We need to pass in the attribute (TreeNodeAttribute) that we are using to identify which properties should be added to the tree. I also needed a way to extract the text for the TreeNode. This is passed as a string of the name of the property that will return the text. I could have relied on the ToString method, but it is unreliable as this often gets used for other purposes, and I may want to use already defined properties that will return a “pretty” version of the text for the TreeNode.

To add an object to the AttributeTreeView control, you need to Populate:

  m_ProjectTree.Populate<TreeNodeAttribute>(projectItem, "Name");

In the above example I call Populate, specifying the attribute to check, and the name of the property to retrieve the text for the TreeNode, as well as the object that I want to display. There are two implementations of Populate. The Populate method shown below will add TreeNodes to a TreeNode parent. The other version of Populate calls this method, but clears existing nodes first and does not require a TreeNode parent.

public void Populate<TAttribute>(object item, TreeNode tnParent,
  string sPropertyForText)
  where TAttribute : Attribute
{
  BeginUpdate();
  TreeNode tn = Add<TAttribute>(item, tnParent, sPropertyForText);
  if (null != tn)
  {
    Nodes.Add(tn);
  }
  EndUpdate();
}

The main work is implemented in the private method Add, as shown below:

private TreeNode Add<TAttribute>(object item,
  TreeNode treeNodeParent, string sPropertyForText)
  where TAttribute : Attribute
{
  if (null != item)
  {
    // See if we can get the property sPropertyForText from the item
    PropertyInfo propertyInfoForText = item.GetType().GetProperty(sPropertyForText);
    // if we can't access the property? it might not be a valid object,
    // or not a valid property so return.
    if (null == propertyInfoForText)
    {
      return null;
    }
    // Create a new tree node, as we know that the object item has
    // a valid property on it.
    TreeNode treeNode = new TreeNode(
        propertyInfoForText.GetValue(item, null).ToString());
    // Store a reference to the actual object as the TreeNode's Tag property
    treeNode.Tag = item;
    // if there's a valid parent add the new treenode (tn) to the parent
    if (null != treeNodeParent)
    {
      treeNodeParent.Nodes.Add(treeNode);
    }
    // Is item an array or container of objects?
    // i.e. if it implements IEnumerable we can enumerate over
    // the objects to see if they can be added to the tree.
    IEnumerable enumerableObject = item as IEnumerable;
    if (null != enumerableObject)
    {
      foreach (object itemInEnumerable in enumerableObject)
      {
        Add<TAttribute>(itemInEnumerable, treeNode, sPropertyForText);
      }
    }
    // Get the item’s properties and see if
    // there are any that have the attribute TreeNodeAttribute assigned to it
    PropertyInfo[] propertyInfos = item.GetType().GetProperties();
    foreach (PropertyInfo propertyInfo in propertyInfos)
    {
      // Check all attribs available on the property
      object[] attribs = propertyInfo.GetCustomAttributes(false);
      foreach (TAttribute treeNodeAttribute in attribs)
      {
        // Try and add the return value of the property to the tree,
        // if the property returns null it will be caught at the
        // beginning of this method.
        Add<TAttribute>(propertyInfo.GetValue(item, null),
            treeNode, sPropertyForText);
      }
    }
    return treeNode;
  }
  return null;
}

Hopefully the comments describe how the method works. This is a very generic way of adding objects to a TreeView control. I plan to extend TreeNodeAttribute to include properties that allow you to specify the image for the TreeNode, if it’s a checkable item, etc. The only problem is that this will then couple the AttributeTreeView control with TreeNodeAttribute. The where cause on the methods will change from...

public void Populate<TAttribute>(object item, TreeNode tnParent,
  string sPropertyForText)
  where TAttribute : Attribute

... to:

public void Populate<TAttribute>(object item, TreeNode tnParent,
  string sPropertyForText)
  where TAttribute : TreeNodeAttribute

Please feel free to pass on your comments or suggestions. The attached sample is a Visual Studio 2008 project, but the code will easily port to earlier versions.

History

  • 30th January, 2008: Initial version

License

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

Share

About the Author

Mike Appleby
Software Developer (Senior) www.avecto.com
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralWhich is better Performance wise Object model using reflection or using XML (XML.linq.XDocument) Pinmemberm_tejas@yahoo.com1-Apr-09 2:21 
QuestionWhat about... PinprotectorMarc Clifton30-Jan-08 3:08 
AnswerRe: What about... Pinmembermaqcs12330-Jan-08 9:16 
GeneralGood Start, but not sure I would us this too often PinmvpSacha Barber30-Jan-08 2:37 
GeneralRe: Good Start, but not sure I would us this too often Pinmembermaqcs12330-Jan-08 9:17 
GeneralDownload PinmemberArmando Airo'29-Jan-08 23:50 
GeneralRe: Download [modified] PinmemberTefik Becirovic30-Jan-08 0:43 

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 | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 30 Jan 2008
Article Copyright 2008 by Mike Appleby
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid