다음을 통해 공유


WPF ObjectDumper and Linq to Sql deferred loading

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: https://code.msdn.microsoft.com/wpfobjectdumper.

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

Mitsu

WPFObjectDumper.zip

Comments

  • Anonymous
    December 24, 2007
    PingBack from http://www.saybird.com/?p=2827

  • Anonymous
    January 04, 2008
    Thanks a lot! This article is really helpful.

  • Anonymous
    March 04, 2008
    can post please post an example that show us how to make the CRUD operation with linq to sql in wpf-UI. Thanks a lot for your article

  • Anonymous
    March 04, 2008
    can post please post an example that show us how to make the CRUD operation with linq to sql in wpf-UI. Thanks a lot for your article

  • Anonymous
    March 19, 2008
    can we use ObjectDumper to get the result of a query in to a var? for exemple: var query = from a in db.Users where a.GroupID == GroupID select a; return (query); what is the type of query?

  • Anonymous
    March 19, 2008
    @Rachid: please, could you detail more what you a would like exactly. Do you mean CRUD using full databinding ? @ilpostino: yes you can ! When using the 'var' C#3 keyword the type is inferred by the compiler. Even if it's a real CLR type, you don't deal with it directly in the code. when returning 'query', you may not know the exact type and simply use object for more convenience. By design, the databinding is defined in text (property names as strings). So it does absolutely do not care about how well defined is the datatype since it's always using to reflection. It's even working if your select returns an anymous type. This is true for WPF but also Winforms and ASP.Net databinding.

  • Anonymous
    June 05, 2008
    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