View Object properties in WPF TreeView

I use Visual studios on almost a daily basis and I love being able to set a breakpoint and dive through the hierarchy of an object and see the property name and values. One thing I’ve struggled with in the past was being able to generate a similar experience for viewing a generic object structure in my application. In the past I found manually generating  a TreeView with all the nodes to be extremely painful and always wanted a simple way to just pass in a random object and expose the property names and values, just like visual studios does automatically.

Today I found myself in a great position, I blocked out the day as I figured I would be busy but ended up having a couple of hours of free time. This ended up being a great opportunity to try playing around with some development projects I had on my TODO list that weren’t necessarily Bing Maps related. After a bit of work I managed to create a great class that built out the object tree information I need to fill a TreeView in WPF just like Visual Studios does when debugging. The best part is that it uses reflection and can work with just about any object. Here is the code for this class that parses out the object tree information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace ToolKit
{
    class ObjectNode
    {
        #region Private Properties

        private string _name;
        private object _value;
        private Type _type;

        #endregion

        #region Constructor

        public ObjectNode(object value)
        {
            ParseObjectTree("root", value, value.GetType());
        }

        public ObjectNode(string name, object value)
        {
            ParseObjectTree(name, value, value.GetType());
        }

        public ObjectNode(object value, Type t)
        {
            ParseObjectTree("root", value, t);
        }

        public ObjectNode(string name, object value, Type t)
        {
            ParseObjectTree(name, value, t);
        }

        #endregion

        #region Public Properties

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public object Value
        {
            get
            {
                return _value;
            }
        }

        public Type Type
        {
            get
            {
                return _type;
            }
        }

        public List<ObjectNode> Children { get; set; }

        #endregion

        #region Private Methods

        private void ParseObjectTree(string name, object value, Type type)
        {
            Children = new List<ObjectNode>();

            _type = type;
            _name = name;

            if (value != null)
            {
                if (value is string && type != typeof(object))
                {
                    if (value != null)
                    {
                        _value = "\"" + value + "\"";
                    }
                }
                else if (value is double || value is bool || value is int || value is float || value is long || value is decimal)
                {
                    _value = value;
                }
                else
                {
                    _value = "{" + value.ToString() + "}";
                }
            }

            PropertyInfo[] props = type.GetProperties();

            if (props.Length == 0 && type.IsClass && value is IEnumerable && !(value is string))
            {
                IEnumerable arr = value as IEnumerable;

                if (arr != null)
                {
                    int i = 0;
                    foreach (object element in arr)
                    {
                        Children.Add(new ObjectNode("[" + i + "]", element, element.GetType()));
                        i++;
                    }

                }
            }

            foreach (PropertyInfo p in props)
            {
                if (p.PropertyType.IsPublic)
                {
                    if (p.PropertyType.IsClass || p.PropertyType.IsArray)
                    {
                        if (p.PropertyType.IsArray)
                        {
                            try
                            {
                                object v = p.GetValue(value, null);
                                IEnumerable arr = v as IEnumerable;

                                ObjectNode arrayNode = new ObjectNode(p.Name, arr.ToString(), typeof(object));

                                if (arr != null)
                                {
                                    int i = 0, k = 0;
                                    ObjectNode arrayNode2;

                                    foreach (object element in arr)
                                    {
                                        //Handle 2D arrays
                                        if (element is IEnumerable && !(element is string))
                                        {
                                            arrayNode2 = new ObjectNode("[" + i + "]", element.ToString(), typeof(object));

                                            IEnumerable arr2 = element as IEnumerable;
                                            k = 0;

                                            foreach (object e in arr2)
                                            {
                                                arrayNode2.Children.Add(new ObjectNode("[" + k + "]", e, e.GetType()));
                                                k++;
                                            }

                                            arrayNode.Children.Add(arrayNode2);
                                        }
                                        else
                                        {
                                            arrayNode.Children.Add(new ObjectNode("[" + i + "]", element, element.GetType()));
                                        }
                                        i++;
                                    }

                                }

                                Children.Add(arrayNode);
                            }
                            catch { }
                        }
                        else
                        {
                            object v = p.GetValue(value, null);

                            if (v != null)
                            {
                                Children.Add(new ObjectNode(p.Name, v, p.PropertyType));
                            }
                        }
                    }
                    else if (p.PropertyType.IsValueType && !(value is string))
                    {
                        try
                        {
                            object v = p.GetValue(value, null);

                            if (v != null)
                            {
                                Children.Add(new ObjectNode(p.Name, v, p.PropertyType));
                            }
                        }
                        catch { }
                    }
                }
            }
        }

        #endregion
    }
}

In your WPF application you can create a simple TreeView that will render this object nicely:

<TreeView Name="ResultTreeView" BorderThickness="0">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type models:ObjectNode}" ItemsSource="{Binding Path=Children}">
            <TreeViewItem>
                <TreeViewItem.Header>
                    <StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
                        <TextBlock Text="{Binding Path=Name}"/>
                        <TextBlock Text=" : "/>
                        <TextBlock Text="{Binding Path=Value}"/>
                    </StackPanel>
                </TreeViewItem.Header>
            </TreeViewItem>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

You can then easily pass in any object into this TreeView by first passing it through the ObjectNode class. Here is an simple example of implementing this object:

List<ObjectNode> nodes = new List<ObjectNode>();
nodes.Add(new ObjectNode("result", r));
ResultTreeView.ItemsSource = nodes;

If you where to pass in a geocode response from the Bing Maps REST services you would end up with something like this:

image

As this is primarily a Bing Maps blog I’ve put together a test for that is great for inspecting the Bing Maps REST Services responses. You can download this tool here.

Advertisements

2 thoughts on “View Object properties in WPF TreeView

    • This code won’t display the DateTime as a string. If you want to do that then you will need to some logic to the beginning of the ParseObjectTree method to convert the DateTime into a formatted string.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s