Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

Data Templates Implementation Outside of WPF

, 28 May 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Data Templates Implementation Outside of WPF CodeProject Code Location The code for this blog post can be downloaded from NonWPFDataTemplatesTests.zip. Introduction Here I continue a series of blog posts regarding implementing WPF concepts outside of WPF. WPF is built around a hierarchy of visual ob

Code Location

The code for this blog post can be downloaded from NonWPFDataTemplatesTests.zip.

Introduction

Here I continue a series of blog posts regarding implementing WPF concepts outside of WPF.

WPF is built around a hierarchy of visual objects the so called Visual Tree. Any WPF control can provide an example of such hierarchy - e.g. a WPF window might contain a grid panel which in turn contains a number of visual objects e.g. buttons and list boxes which in turn contain some text etc.

The object hierarchy is usually built in XAML with the help of numerous techniques such as bindings, Control Templates and Data Templates. Data Templates with the help of bindings allow to translate some non-visual objects into visual objects with the hierarchy defined by XAML. These resulting visual objects usually mimic the behaviors of the original non-visual objects – e.g. Changing a property on a non-visual object would result in the corresponding visual property change, adding or removing an object to or from an observable collection would result in a corresponding item being added or removed from e.g. a ListView.

This blog entry will show how to use concepts similar to WPF Data Templates outside of WPF and apply them for creating in XAML purely non-visual objects.

I’d like to stipulate that the utility of the concepts introduced here might not be quite obvious yet. The usage of non-visual data templates will be explained in the future blog posts.

Also I would like to express gratitude to the WPF re-implementation attempt called Avalonia. It would have taken me much longer to understand the inner workings of XAML functionality without this project and also I am using a modified version of several of its classes: AvalonXamlReader, ContentLoader and TemplateContent in order to create the non-visual Data Templates.

Data Hierarchy Code Description

Data Hierarchy

First of all let me describe the non-visual hierarchy that we want to build.

The WPF’s visual hierarchy is based on the natural containment of visual objects. As was stated above a window might contain a panel – a panel further might contain other panels and controls etc.

The non-visual hierarchy that we deal with is Property Map and the classes that represent that hierarchy are contained under NP.Paradigms.DataHierarchy project. In a sense it mimics the data hierarchy within JSON code: there are Fields, Property Maps, and Collections.

A Field would only contain a value of type object.

A Property Map corresponds to a C# object - it has a number of named properties each has a value. The values can be plain Fields, other Property Maps or Collections. The name of a property is unique within the map object. I implemented such maps as a Dictionary with string keys corresponding to the name of the property and values – the property value. In JSON such object would be shown within curly brackets:

{ name1 : value1, name2 : value2 }

Collection is a collection of objects (usually of similar structure) that can be either Property Maps or Fields. In JSON collections are denoted by square brackets:

[ value1, value2 ]

Clearly our Data Hierarchy can be used to represent a very wide range of models – in particular anything that can be represented by JSON or C# classes and collections can be represented by the Data Hierarchy.

Classes Describing the Data Hierarchy

As was mentioned above all the classes describing our Data Hierarchy model are locate under NP.Paradigms.DataHierarchy project.

All the Data Hierarchy classes implement IPresentationTreeNode interface. This interface has Parent property that points from the current object to its parent within the Data Hierarchy. IPresentationTreeNode also extends IDataContextContainer interface that defines DataContext property that all the members of the hierarchy should implement. Just like in WPF, the DataContext property contains the data object from which the member of the hierarchy gets its values via binding.

There is also IPresentationTreeNodeWithChildren<T> interface that extends IDataContextContainer and in addition also defines a collection Children.

FieldPresenter class corresponds to a single value Data Hierarchy node. Its property TheValue contains the corresponding value. Its subclass TextFieldPresenter also allows formatting the values for text presentation.

CollectionPresenter defines a collection within Data Hierarchy by its property Children.

PropertyMapPresenter represents the Property Map nodes within Data Hierarchy. It implements IDictionary so that one can use the indexer notations (square brackets) to define the name-value mappings, e.g: myMap["Name"] = value

Note that classes that implement Data Hierarchy nodes fire PropertyChanged events when their properties change. Also Children property of the CollectionPresenter is implemented as ObservableCollection and correspondingly fired CollectionChanged event when the items are changed within the collection (removed, added or moved). This is necessary so that the bindings that have the Data Hierarchy nodes as source would work.

Classes for XAML Composition using Data Templates

Classes used for XAML composition are also defined under NP.Paradigms.DataHierarchy project.

DHierarchyContentControl and DHierarchyItemsControl correspond to the WPF’s ContentControl and ItemsControl classes.

DHierarchyContentControl defines properties Content and ContentTemplate. ContentTemplate defines the sub-hierarchy of the node while Content property allows to switch the data context of its sub-nodes.

DHierarchyItemsControl defines properties ItemsSource and ItemTemplate. ItemsSource should be bound to a collection of objects. ItemTemplate specified the structure of each of the sub-nodes within DHierarchyItemsControl‘s Children collection. The resulting Children collection behaves similar to the ItemsSource collection if an item is added or removed from ItemsSource the corresponding item will be added or removed from the Children collection.

There is a number of classes for reading and processing XAML data templates. Class DHierarchyDataTemplate is used for storing the Data Hierarchy data templates within DHierarchyContentControl and DHierarchyItemsControl. DHierarchyTemplateContent together with DHierarchyContentLoader and AvalonXamlReader control loading the data templates from XAML.

Usage Code Description

Here we describe how the Data Hierarchy code with data template capabilities can be used.

The mock up data is contained under MockUpObjects. Here we consider only two classes: Organization and Person. Both are very simple Organization contains its name and a collection of people. Person simply contains first and last name of the person.

The main project in NonVisualDataTemplates. It defines an organization as its resource:

  <MockUps:Organization x:Key="TheWonkaChocolateFactoryOrganization" />

Around the organization object it defines the Data Hierarchy data template. While on top of the Data Hierarchy data template it defines a visual representation (just in order to be able to see that it works).

Data Hierarchy representation is defined by the following templates:

<hierarchy:DHierarchyDataTemplate x:Key="EmployeeNonVisualDataTemplate">
    <hierarchy:PropertyMapPresenter>
        <hierarchy:FieldPresenter x:Key="FirstName" 
                                  TheValue="{winparadigms:Bind SourcePath=FirstName, 
                                                               SourceMode=DataContext}"/>
        <hierarchy:FieldPresenter x:Key="LastName" 
                                  TheValue="{winparadigms:Bind SourcePath=LastName, 
                                                               SourceMode=DataContext}"/>
    </hierarchy:PropertyMapPresenter>
</hierarchy:DHierarchyDataTemplate>

This template define the Data Hierarchy representation of a person (employee)

<hierarchy:DHierarchyDataTemplate x:Key="TheOrganizationTemplate">
    <hierarchy:PropertyMapPresenter>
        <hierarchy:FieldPresenter x:Key="OrgName" 
                                  TheValue="{winparadigms:Bind SourcePath=OrgName, 
                                                               SourceMode=DataContext}"/>
        <hierarchy:DHierarchyItemsControl x:Key="People"
                                          ItemsSource="{winparadigms:Bind SourcePath=People, 
                                                                          SourceMode=DataContext}"
                                          ItemTemplate="{StaticResource EmployeeNonVisualDataTemplate}"/>
    </hierarchy:PropertyMapPresenter>
</hierarchy:DHierarchyDataTemplate>

This template defines the whole organization with multiple employees.

DHierarchyContentControl ‘marries’ the organization template and the organization data:

<hierarchy:DHierarchyContentControl ContentTemplate="{StaticResource TheOrganizationTemplate}"
                                       Content="{StaticResource TheWonkaChocolateFactoryOrganization}"
                                       x:Key="TheDHierarchyContentControl"/>

In order to visualize the results, a plain visual WPF hierarchy is build around the DHierarchyContentControl:

<Grid Grid.Row="0" 
      DataContext="{winparadigms:Bind Source={StaticResource TheDHierarchyContentControl}}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <TextBlock Text="{winparadigms:Bind SourcePath=Child.-OrgName-.TheValue, SourceMode=DataContext}"
               Margin="10,0,0,0"/>

    <Border Grid.Row="1"
        BorderBrush="Black"
        BorderThickness="2"
        Margin="5">
        <ItemsControl ItemsSource="{winparadigms:Bind SourcePath=Child.-People-.Children, SourceMode=DataContext}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{winparadigms:Bind SourcePath=Child.-FirstName-.TheValue, SourceMode=DataContext}" />
                        <TextBlock Text="{winparadigms:Bind SourcePath=Child.-LastName-.TheValue, SourceMode=DataContext}" 
                                   Grid.Column="1"
                                   Margin="20,0,0,0"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Border>
</Grid>

Note that we are using Bind markup extension instead of Binding. This extension has been described in some of the previous blog entries.

Also note that ‘-’ character around a path link specifies that this path link is actually a key inside a Dictionary for the Bind markup extension. Also SourceMode=DataContext means that we are binding to the DataContext property (by default Bind extension binds to the object on which it is defined, not to its DataContext).

There are two buttons at the bottom of the application that allow to modify the original Organization object. The very simple for modifying the original Organization is located in MainWindow.xaml.cs code behind file. You can add an employee or you can replace its People collection with another collection. These changes will cause changes in Data Hierarchy representation and those in turn will cause the visual representation to change.

After running the application (and pressing a button to add an employee) you’ll see the following picture:

Non-WPF Data Template Test App

Conclusion

We’ve shown implementing XAML data templates for non-WPF (and even non-Visual) objects.

License

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

Share

About the Author

Nick Polyak
Architect AWebPros
United States United States
I have 15 years of experience developing enterprise software, starting from C++ and Java on UNIX and moving towards C# on Windows platforms.
I am fascinated by the new .NET technologies especially WPF, Silverlight and LINQ.
Recently I decided to make a move and start my own contracting consulting and mentoring company AWebPros.
I can be contacted via my web site awebpros.com or through my blog at nickssoftwareblog.com

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150123.1 | Last Updated 28 May 2014
Article Copyright 2014 by Nick Polyak
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid