Video: WPF Object dumper demo

The official C# 3.0 samples are providing a very useful ObjectDumper class that allows displaying any kind of object on the console output. All the child properties are also displayed and you can even define how deep you want to go through child properties.

In this sample, I will show how to achieve quite the same using Windows Presentation Foundation.

Actually, I will show two specific technologies:

  • Linq to Sql deferred loading
  • WPF data driven UI technology (see also this previous post)

In WPF, you can define in resources a DataTemplate with a DataType specified but without key. Then the template will be automatically associated with any content control that displays a content whose type is the same as the DataType of the template.

The goal is to display a tree of objects with child properties and so on as long as you expland child nodes.

We could easilly create specific data templates for each type (Customer, Order, etc) :

<HierarchicalDataTemplate DataType = "{x:Type local:Customer}" ItemsSource = "{Binding Path=Orders}"> <TextBlock Text="{Binding Path=CompanyName}" /> </HierarchicalDataTemplate>

You can notice that the HierarchicalDataTemplate only offers one ItemsSource to go child items.

The goal here is to provide a generic solution. That's why I've chosen to create some proxies classes to encapsulate any class and any properties.

To achieve this we have to solve a few things:

  • Make the properties of an object appear as a collection of the object (Properties[]) so we can easilly bind them.
  • Make the properties which are subcollections (IEnumerable) a new source (Items[]).
  • Make the properties which are a class (non primitive type) a new source.

All these proxies will provide a logical view that fits to be bound directly to the HierarchicalDataTemplate. This is typically what a view layer is supposed to do: provide views on to objects that makes binding and UI management easier.

The ClassPropertiesProxy class is the entry point. It offers a view for binding for a given object. All the properties of the source object are exposed in a collection of new descriptors.
Depending on the type of the property, some different descriptors are created. BaseDescriptor is the base class of all the descriptor.

  • If the property is a collection (IEnumerable), we provide a EnumerationPropertyDescriptor. This class provides a header for all the child items. Each child item is a ClassPropertiesProxy again.
  • If the property is a class (non primitive type), we provide a ClassPropertiesProxy.
  • If the property is a primitive type, we create a SimplePropertyDescriptor which represents the leaves of our tree (no children).

image

No let's go back to the UI. WPF will only have to deal with those few known types (instead of Customer, Order, etc), so we can build generic template on the top of them.

Template for each object:

<HierarchicalDataTemplate DataType = "{x:Type local:ClassPropertiesProxy}" ItemsSource = "{Binding Path=Properties}"> <TextBlock Foreground="Green" Text="{Binding Path=Name}" /> </HierarchicalDataTemplate>

Template for each SubCollection:
<HierarchicalDataTemplate DataType = "{x:Type local:EnumerationPropertyDescriptor}" ItemsSource = "{Binding Path=Items}"> <StackPanel Orientation="Horizontal"> <TextBlock x:Name='tbName' FontWeight="Bold" Foreground="Gray" Text="{Binding Path=Name}"/> <TextBlock Text=" : " /> <TextBlock x:Name="tbType" Foreground="Gray" FontStyle="Italic" Text="{Binding Path=Type}"/> </StackPanel> </HierarchicalDataTemplate>

Template for each simple value:
<DataTemplate DataType="{x:Type local:SimplePropertyDescriptor}"> <StackPanel Orientation="Horizontal"> <TextBlock x:Name="tbName" Foreground="Gray" Text="{Binding Path=Name}" /> <TextBlock Text=" = " /> <TextBlock Foreground="Red" Text="{Binding Path=Value}" /> <TextBlock Text=" : " /> <TextBlock x:Name="tbType" Foreground="Gray" FontStyle="Italic" Text="{Binding Path=Type}"/> </StackPanel> </DataTemplate>

Everything is now ready. We have to provide a source for testing. Linq to Sql provides a very interesting feature that is DeferredLoading. In case you have relationships between your entities, deferred loading allows Linq to Sql to fill dynamically unloaded relations on the fly.

On the fly means when the relation is readen. It can be raised by code but also by binding (which is quite the same).

If we use a Linq to Sql query as the source of our ObjectDumper sample, the result is very nice because we have a generic visual browser for all the data. Moreover, we do not load the whole database but the binding while raise the deferred loading automatically to make Linq to Sql only retrieve the data we need to display.

var q = from c in db.Customers where c.City == "London" select c; var list = q.ToList(); ObjectDumper.Write(list, treeView1, "Customers"); ObjectDumper.Write(list, rootMenuItem, "Customers");

ObjectDumper.Write is the entry point to connect any data to an ItemsControl. If the ItemsControl supports HierarchicalDataTemplate, then the whole things run automatically.

image

Another surprising control that supports HierarchicalDataTemplates is the MenuItem !

image

You can find the VS2008 source code here: http://code.msdn.microsoft.com/wpfobjectdumper.

Hey we are the 24th of December today: Merry Christmas to all of you !

Mitsu