0% found this document useful (0 votes)
17 views

Presentation Patterns For XAML Based Applications

This document describes design patterns for presentation layers in XAML-based applications like WPF, Silverlight, and Windows Phone. It discusses patterns for actions and commands, performance with data containers, loading data asynchronously and on demand, selection tracking in collections, navigation and editing views, and item models. Examples are provided using an example library to demonstrate how these patterns can be implemented for reusable UI components.

Uploaded by

Jani Giannoudis
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

Presentation Patterns For XAML Based Applications

This document describes design patterns for presentation layers in XAML-based applications like WPF, Silverlight, and Windows Phone. It discusses patterns for actions and commands, performance with data containers, loading data asynchronously and on demand, selection tracking in collections, navigation and editing views, and item models. Examples are provided using an example library to demonstrate how these patterns can be implemented for reusable UI components.

Uploaded by

Jani Giannoudis
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

Presentation Patterns for XAML based Applications

Design patterns on the presentation layer for WPF, Silverlight and Windows Phone
applications.
24/05/2012 - Version 1.1.0.0

Introduction
In the recent years, a variety of different patterns, like MVVM/MVC and Command, have
established themselves for the development of user interface oriented applications and been put
to good use. This article describes specialized design patterns, which can be of use in user
interface development of XAML based applications.

By means of an example library, several application examples from everyday life will be used to
demonstrate these design patterns and to give an idea, how these can be put into code for
reusable components.

Aside from the benefits of increasing reuse, such design patterns help to reduce the complexity of
a system. They support the design process for future development because they advocate the
common understanding of the involved designers.

During implementation of the design patterns, a variety of aspects in regard to communications,


performance, resource management and composite development for WPF/Silverlight/Windows
Phone 7 had to be considered. The following areas will be addressed:

• Actions and synchronous/asynchronous commands

• Performance with Containers for objects bound to UI elements


→ Improve the ObservableCollection performance - 2 to 10 times faster

Page 1 von 30 Version: 1.1.0.0


• Synchronous/asynchronous loading of data

• Loading of data on demand


→ TreeView with lazy loading

• Controlling the selection in a collection


→ ListBox, ComboBox and DataGrid selection tracking
• Container views for navigation and editing
• Item Model – a view model abstraction
• Hierarchical management of objects of diverse types

• Editors for editing elements


→ TreeView and DataGrid with CRUD operations
As mentioned before, the library presented in this article should be taken as one possible variant
of how to implement the given design patterns on the presentation layer. To stay focused on the
important aspects with this extensive subject, no additional tools and components have been used
and only standard .NET framework items have been used. Although the library can be used as it is,
an adaptation to the target domain is considered reasonable.

The design patterns can be viewed isolated and can be put to use in an existing architecture.

Action and Command

Action
An Action describes a simple and self-contained operation and leaves its implementation open. It
serves as a contract between an Action Provider and an Action Consumer:

The main characteristic of an Action is its concentration on an actual situation or an operation.


Several actions can be combined into an Action Group by topic, although they usually don't have
any other relationship with each other. The goal is to reduce dependencies to lower overall
complexity. The Actions and the Action Groups can be viewed as building blocks. The combination
of these will be determined by an Action Consumer, for example the view model.

The following examples demonstrate the usage of the Action pattern:

Page 2 von 30 Version: 1.1.0.0


Because an Action is a neutral definition, standardized components can be developed which can
then be reused for different applications. For the further course we will use a library with the
following Action Groups and Actions:

Action Group Action

Data • Clearable
• Loadable
• Refreshable
• Resettable
• Selectable
• Sortable
• Comparer Sortable
• Comparison Sortable
• Updateable

Item • Item Create


• Item Refresh
• Item Edit
• Item Delete

Media • Play
• Pause
• Resume
• Stop

Navigation • Expandable
• Collapsible

The implementation of an Action is done in the form of a simple interface:


// ------------------------------------------------------------------------
public interface IExpandable

Page 3 von 30 Version: 1.1.0.0


{

// ----------------------------------------------------------------------
bool IsExpandable { get; }

// ----------------------------------------------------------------------
void Expand();

} // interface IExpandable

A TreeView will serve as an example of an Action Provider:


// ------------------------------------------------------------------------
public class MyTreeView : TreeView, IExpandable
{

// ----------------------------------------------------------------------
bool IExpandable.IsExpandable
{
get
{
MyTreeViewItem selectedItem = SelectedTreeViewItem;
return selectedItem != null && !selectedItem.IsExpanded &&
selectedItem.Items != null && selectedItem.Items.Count > 0;
}
} // IExpandable.IsExpandable

// ----------------------------------------------------------------------
void IExpandable.Expand()
{
MyTreeViewItem selectedItem = SelectedTreeViewItem;
if ( selectedItem != null )
{
selectedItem.IsExpanded = true;
}
} // IExpandable.Expand

} // class MyTreeView

The implementation of an Action Consumer happens through a command, which will be described
in the following chapter.

Command
The Command design pattern, introduced by the GoF, is present in the .NET framework through
the interface ICommand. A command starts a sequence of steps which is usually invoked by the
user through some user interface control. Commands can be provided by different elements:

• Class utilities/command containers like for example


System.Windows.Input.ApplicationCommands

• View models
• UI controls
The trigger of a Command is usually something like a button, which is connected to the command
by a binding to the property Command. Often the availability of the command is visualized in the
UI element through ICommand.CanExecute (enabling). The invocation of the command happens
by a call to the method ICommand.Execute.

Page 4 von 30 Version: 1.1.0.0


The following derivation extends ICommand by the following example aspects:
// ------------------------------------------------------------------------
public interface ICommand : System.Windows.Input.ICommand, INotifyPropertyChanged, IDisposable
{

// ----------------------------------------------------------------------
event EventHandler Started;

// ----------------------------------------------------------------------
event EventHandler Finished;

// ----------------------------------------------------------------------
string Name { get; }

// ----------------------------------------------------------------------
bool IsExecuting { get; }

// ----------------------------------------------------------------------
void Refresh( object parameter );

} // interface ICommand

Identification The command has a name.

Asynchronous execution Depending on technology and usage scenario, the command


will execute an asynchronous operation. By extension of
Execute and the execution status IsExecuting, the command
provides a common base for synchronous and asynchronous
execution environments.

By registering to the events Started and Ended, external


observers can keep track of the execution status.

Status Availability of the command is signaled through IsAvailable.


The variant implemented here ensures that the command is
not available during execution. The method Refresh can be
used to explicitly refresh the availability status.

State changes To communicate changes of values, the .NET interface


INoftifyPropertyChanged is supported.

Resource management To ensure proper release of any held resources, the .NET
interface IDisposable is implemented.

To simplify the handling of multiple commands (execution control, resource management, etc.),
they can be registered in a ICommandCollection.

Action Command
The design pattern Action Command combines the patterns for Action and Command. In general,
an Action Command should only cover a single Action. This helps to reduce interconnectedness

Page 5 von 30 Version: 1.1.0.0


and complexity. This limitation also leads to better reusability and is the basis for libraries with
predefined commands such as for example the ApplicationCommands of the .NET framework.

The following example demonstrates the implementation of an Action Command:


// ------------------------------------------------------------------------
public class ExpandCommand : Command
{

// ----------------------------------------------------------------------
public ExpandCommand( IExpandable expandable )
{
if ( expandable == null )
{
throw new ArgumentNullException( "expandable" );
}
this.expandable = expandable;
} // ExpandCommand

// ----------------------------------------------------------------------
public IExpandable Expandable
{
get { return expandable; }
} // Expandable

// ----------------------------------------------------------------------
protected override bool IsAvailable( object parameter )
{
return Expandable.IsExpandable;
} // IsAvailable

// ----------------------------------------------------------------------
protected override bool DoExecute( object parameter )
{
Expandable.Expand();
return true; // finished
} // DoExecute

// ----------------------------------------------------------------------
// members
private readonly IExpandable expandable;

} // class ExpandCommand

The article WPF Command-Pattern Applied describes more usage scenarios of the Command
design pattern.

Collections

Item Collection
An Item Collection is a container which is specialized on UI elements with bindings. The declaration
of Item Collection covers several aspects:

• List functionality: ICollection<TItem>


• Sorting: ISortable, IComparisonSortable<TItem> and IComparerSortable<TItem>
• Messaging: INotifyCollectionChanged and INotifyPropertyChanged

Page 6 von 30 Version: 1.1.0.0


• Data updating: IUpdateable

• Resource management: IDisposable


The following interface IItemCollection describes an Item Collection:
// ------------------------------------------------------------------------
public interface IItemCollection<TItem> : ICollection<TItem>,
ISortable, IComparisonSortable<TItem>, IComparerSortable<TItem>,
INotifyCollectionChanged, INotifyPropertyChanged, IUpdateable, IDisposable
{

// ----------------------------------------------------------------------
void AddAll( IEnumerable<TItem> items );

// ----------------------------------------------------------------------
void Replace( TItem oldItem, TItem newItem );

} // interface IItemCollection

Sorting The IItemCollection offers the capability to sort its elements by


different means. This sorting is permanent and should not be
confused with the one of the .NET ICollectionView, which is
used for sorted display.

Resource management Implementation of the .NET interface IDisposable helps in


releasing any held resources. During disposal of the list, all
elements will be disposed which themselves support the
IDisposable contract.

AddAll Adds all elements of an enumerable source.

Replace Replaces an element by another one.

Aside from the various helper methods, the primary benefit of using Item Collection lies in its
support for the Updateable action, which helps to ensure greatly increased performance upon
bulk operations.

Many UI elements (ListBox, DataGrid, TreeView etc.) are designed to handle a list of elements. The
property ItemsSource binds the list to the UI element. If the content of the list changes at runtime,
this will be reflected in the corresponding UI element. The mechanism for this is based on the
interface INotifyCollectionChanged. The list, in this case the ObservableCollection, implements
INotifyCollectionChanged and the UI element checks whether the data source implements this
interface. Is that the case, the notifications of the list will be received and put to display. The
sequence of events looks like this:

Page 7 von 30 Version: 1.1.0.0


To reduce the amount of communication between the container and the UI element, the Item
Collection extends the standard list with an update mechanism which is described by the action
Updateable. This will lead to the following sequence of events:

Through the methods BeginUpdate and EndUpdate (greetings to VisualBasic), the notifications
sent to the UI elements will be coordinated manually. Between these points in time, any number
of arbitrary operations can be performed on the container (Add, Replace, Sort, Clear ...), without
the UI element being notified about them. Final updating of the user interface happens at the end
of the update sequence, which leads to an optimization in regard to layouting and rendering.
// ----------------------------------------------------------------------
public void LoadCustomers()
{
Customers.BeginUpdate();
Customers.Clear();
Customers.Add( new CustomerModel( "Joe", "Smith" ) );
Customers.Add( new CustomerModel( "Mary", "Jones" ) );
Customers.Add( new CustomerModel( "Lisa", "Black" ) );
Customers.Add( new CustomerModel( "Peter", "Brown" ) );

Page 8 von 30 Version: 1.1.0.0


Customers.EndUpdate();
} // LoadCustomers

The following recommendations should be followed in regard to the usage of


BeginUpdate/EndUpdate:

• It makes most sense with bulk operations like loading, merging, etc.
• It doesn't make sense when operating on single list elements with Add, Insert or Remove
• The calls always have to be balanced, which should be considered in exception handling
Measurements have shown that - depending on usage scenario - the use of Item Collection can
lead to a tremendous improvement in performance. However, a general statement about the
performance gains cannot be given, as this depends on various factors:

• The number and order of operations during the update sequence


• The binding method: DependecyProperty or INotifiyPropertyChanged
• The way how a UI element renders a binding value
• The implementation of UI elements is different for WPF and Silverlight
• The Silverlight runtime: in browser or Out-Of-Browser
To get a reliable statement about the achievable performance gains, an individual test run in the
targeted usage scenario is required.

The following compilation of results shows some measurements of an Item Collection which is
bound to a ListBox, where each list element has 2 bindings. The measurements of the example
ListBox Binding show the improvements of the loading times for bindings to a DependencyProperty
DP, as well as for bindings using the INotifyPropertyChanged INPC interface:
Load DP DP Update DP Update INPC INPC Update INPC Update INPC INPC Update
Item Count [ms] [ms] vs. [ms] [ms] vs. vs. vs.
DP INCP DP DP Update
WPF
1'000'000 16'966 8'370 203% 10'522 3'538 297% 161% 237%
ItemCollection
WPF
100‘000 17'395 2'180 798% 19'687 1'853 1063% 88% 118%
CollectionView
WPF
10‘000 750 217 346% 1'066 182 584% 70% 119%
CollectionView

Silverlight 1'000'000 33'316 24'035 139% 13'962 8'523 164% 239% 282%

Silverlight
1'000'000 29'599 21'820 136% 12'928 7'342 176% 229% 297%
OOB
Windows
100'000 11'827 10'937 108% 6'419 5'382 119% 184% 203%
Phone 7

Page 9 von 30 Version: 1.1.0.0


Test-Computer: ThinkPad T410s, i5M520, 4 GB RAM, 120 GB SSD HD, Win7-64, Windows-Index 4.4

The measurement results for the WPF ListBox show, that especially with an ICollectionView and
large item numbers, a striking improvement of the performance can be achieved. The Silverlight
ListBox also shows a significant improvement, whereas on Windows Phone 7 the optimization is
hardy discernible.

The next table shows the results for the DataGrid Binding example, which measures the
improvement of load times of an Item Collection which is bound to a DataGrid. Each element in
the DataGrid has 5 bindings:
Load DP DP Update DP Update INPC INPC Update INPC Update INPC INPC Update
Item Count [ms] [ms] vs. [ms] [ms] vs. vs. vs.
DP INCP DP DP Update

WPF 1'000'000 17'277 8'478 204% 11'099 3'523 315% 156% 241%

Silverlight 1'000 307 166 184% 254 156 163% 121% 107%

Silverlight 10'000 2'148 338 635% 1'940 177 1098% 111% 192%

Silverlight 100'000 202'375 1'851 10935% 185'641 484 38382% 109% 383%

Silverlight
1'000 213 125 171% 192 109 176% 111% 114%
OOB
Silverlight
10'000 1'690 260 650% 1'534 140 1093% 110% 185%
OOB
Silverlight
100'000 198'116 1'695 11688% 183'494 452 40566% 108% 375%
OOB

The WPF DataGrid shows a similar improvement as with the ListBox, independent of the number
of items. With the Silverlight DataGrid however, the optimization stands in relation to the number
of items. The more items are in the list, the bigger the performance gain.

In a practical situation, many more factors have a considerable influence on overall load times, like
communication, services, reading data, etc. Preparation of the UI is however responsible for a
substantial part of the overall processing time, which leads to a noticeable improvement of overall
performance when this optimization is used.

Page 10 von 30 Version: 1.1.0.0


Item Collection View
Many applications offer the possibility to select/focus an element of a list, which will then be used
for further operations (editing, deleting, copying, etc.). Such cases are supported by the Item
Collection View with the following functionality:

In contrast to the .NET ICollectionView, which is designed on top of an IEnumerable list for
navigation, filtering and grouping, the Item Collection View specializes on lists bound to UI, which
support the INotifyCollectionChanged interface. The following implementation solely concentrates
on navigation and doesn't support filtering and grouping:
// ------------------------------------------------------------------------
public interface IItemCollectionView<out TItemCollection, TItem> :
IEnumerable<TItem>, INotifyCollectionChanged, INotifyPropertyChanged, IDisposable
where TItemCollection : class, INotifyCollectionChanged, IList<TItem>
{

// ----------------------------------------------------------------------
event CurrentChangingEventHandler CurrentChanging;

// ----------------------------------------------------------------------
event EventHandler CurrentItemChanged;

// ----------------------------------------------------------------------

Page 11 von 30 Version: 1.1.0.0


TItemCollection Items { get; }

// ----------------------------------------------------------------------
ViewSyncMode SyncMode { get;}

// ----------------------------------------------------------------------
TItem CurrentItem { get; set; }

// ----------------------------------------------------------------------
int CurrentPosition { get; }

// ----------------------------------------------------------------------
bool SyncCurrentToNewItem { get; set; }

// ----------------------------------------------------------------------
void EnsureCurrent();

// ----------------------------------------------------------------------
bool MoveCurrentToFirst();

// ----------------------------------------------------------------------
bool MoveCurrentToLast();

// ----------------------------------------------------------------------
bool MoveCurrentToNext();

// ----------------------------------------------------------------------
bool MoveCurrentToPosition( int position );

// ----------------------------------------------------------------------
bool MoveCurrentToPrevious();

} // interface IItemCollectionView

The property CurrentItem represents the pointer and can be changed manually or via binding. The
following example synchronizes the selection of the DataGrid with the CurrentItem:
<DataGrid
ItemsSource="{Binding View.Items}"
SelectedItem="{Binding View.CurrentItem, Mode=TwoWay}">
</DataGrid>

The position of the pointer can be controlled with the various MoveCurrentToXxx operations.

Upon changes to the Item Collection, the pointer can be synchronized using EnsureCurrent. The
switch SyncCurrentToNewItem (default=true) determines whether the CurrentItem pointer should
be adjusted when a new element is inserted. Additionally, the view guarantees that the pointer
retains its position upon modifications such as deletion of the current item. This prevents arbitrary
jumping of the active element.

With automatic synchronization of the CurrentItem turned on, the property SyncMode can be used
to control the mode of synchronization:
• ViewSyncMode.Synchronous: Adjustment of CurrentItem happens synchronously

• ViewSyncMode.Asynchronous: Adjustment of CurrentItem happens asynchronously

Asynchronous adjustment is necessary when Items and CurrentItem of the view are bound to the
same UI element. In such cases the asynchronous adjustment ensures that the UI element will not
reset the value of CurrentItem (Two-Way Binding) upon changes to the list.
Page 12 von 30 Version: 1.1.0.0
Item Collection Selection
Some applications offer the possibility to select multiple elements and use them for further
operations. The pattern Item Collection Selection offers, based on the action Selectable, the
control of element selection:
// ------------------------------------------------------------------------
public interface ISelectable
{

// ----------------------------------------------------------------------
event SelectionChangedEventHandler SelectionChanged;

// ----------------------------------------------------------------------
IEnumerable SelectedItems { get; }

// ----------------------------------------------------------------------
void UpdateSelection( IEnumerable removedItems, IEnumerable addedItems );

} // interface ISelectable

Its declaration happens in the interface IItemCollectionSelection:


// ------------------------------------------------------------------------
public interface IItemCollectionSelection<out TItemCollection, TItem> : IEnumerable<TItem>,
INotifyCollectionChanged, INotifyPropertyChanged, ISelectable
where TItemCollection : class, INotifyCollectionChanged, ICollection<TItem>, new()
{

// ----------------------------------------------------------------------
new TItemCollection SelectedItems { get; }

} // interface IItemCollectionSelection

The example Selection demonstrates the usage of the Item Collection Selection pattern.

View Models
The following chapter introduces several patterns for representing view models:

Page 13 von 30 Version: 1.1.0.0


Item Model
The Item Model design pattern is used for describing objects which are designed for
representation in a UI. Communication between the UI elements and the Item Model happens
through actions (see chapter on Commands) and bindings to properties. Bindings are used to
display property values in the UI and to change them. The binding of a value happens either
through a DependencyProperty DP or with the help of the interface INotifyPropertyChanged INPC.

Which variant makes more sense depends on the situation at hand. A general recommendation
states that DP are primarily used with UI elements and INPC for data centered objects. Because
the Item Model is a construct which should cover both categories, it is not always possible to state
a clear choice. The following overview offers some help for deciding which is appropriate and lists
several aspects with their respective advantages and disadvantages:

Aspect Statement DP INPC

Inheritance DP require to inherit from the base class DependencyObject - +


which is not always desirable or achievable. The WPF variant of
DependencyObject seals the methods Equals and GetHashCode
which can lead to undesired restrictions in the derivations.

Read-Only Values of DP can always be modified through bindings. There - +


Values are values however, which should only be changed by the item
itself, like for example a loading state. In such cases INPC should
be used.

Threading Because of the base class DependencyObject, DP should only be - +


used from a single thread.

Serialization Serialization of the DP base class DependencyObject is not - +


possible with the .NET serialization. This can be circumvented
with the XamlReader and XamlWriter tools.

Trigger INPC offers more flexibility in the control of when execution - +


happens as well as the possibility to combine the changes for
multiple values.

Testing For testing, the restrictions of DependencyObject in regard to - +


threading become relevant, which prevents automated testing.

Performance While Microsoft recommends DP to improve performance ? ?


(MSDN), the measurements for this article have shown that
INPC often leads to improved behavior (see chapter Item
Collection).

Readability/ Is a matter of taste. Beginners usually find the DP pattern easier ? ?

Page 14 von 30 Version: 1.1.0.0


Simplicity to understand.

Resources DP supports the assignment of a value from a resource. + -

Styling/ DP properties can be changed via styles (using setters). + -


Templating

Animation Only DP can be animated. + -

Metadata DP can be changed by derivations, for example for the + -


Overriding assignment of a different style.

Sources: DP vs. INPC, WPF-DP on MSDN, Silverlight-DP on MSDN

To be able to treat an Item Model in a standardized way, the differences between DP and INPC
have to be hidden and the definition packed into a neutral structure. Interfaces are the standard
choice for this and provide the basis for the abstract definition of the Item Model:
// ------------------------------------------------------------------------
public interface IItemModel : INotifyPropertyChanged, IDisposable
{

// ----------------------------------------------------------------------
TDisposable RegisterDisposable<TDisposable>( TDisposable item )
where TDisposable : IDisposable;

// ----------------------------------------------------------------------
void UnregisterDisposable<TDisposable>( TDisposable disposable, bool dispose = true )
where TDisposable : IDisposable;

} // interface IItemModel

The definition is based on the interface INotifyPropertyChanged because this can be used in both
model variants (DP and INPC). To support proper release of resources, the IDisposable interface is
supported. With the help of the utility method RegisterDisposable it is possible to register
resources which should also be released when the Item Model is disposed.

For the support of both implementation variants, the base classes Dependency.ItemModel and
Notifiable.ItemModel are provided.

The following derivation demonstrates the combination of the design patterns Item Model, Action
and Command:
// ------------------------------------------------------------------------
public class MyModel : ItemModel, IRefreshable
{

// ----------------------------------------------------------------------
public MyModel()
{
refreshCommand = RegisterDisposable( new RefreshCommand( this ) );
} // MyModel

// ----------------------------------------------------------------------
public ICommand RefreshCommand
{
get { return refreshCommand; }
} // RefreshCommand

Page 15 von 30 Version: 1.1.0.0


// ----------------------------------------------------------------------
void IRefreshable.Refresh()
{
// refresh data
} // IRefreshable.Refresh

// ----------------------------------------------------------------------
// members
private readonly RefreshCommand refreshCommand;

} // class MyModel

Data Item Model


The Data Item Model is a specialization of Data Model with the added capability to support the
load operation:
// ------------------------------------------------------------------------
public interface IDataItemModel : IItemModel, ILoadable
{

// ----------------------------------------------------------------------
event EventHandler Loading;

// ----------------------------------------------------------------------
event EventHandler Loaded;

} // interface IDataItemModel

Data Item Model both supports the synchronous and the asynchronous loading of its data.
Corresponding to the Item Model, both implementation variants Dependency.DataItemModel and
Notifiable.DataItemModel are provided.

The following example demonstrates the synchronous loading of data:


// ------------------------------------------------------------------------
public class MyModel : DataItemModel
{

// ----------------------------------------------------------------------
protected override bool DoLoad()
{
// data load
LoadDataSync();
return true; // true = load finished -> synchronous
} // DoLoad

} // class MyModel

The following example demonstrates the asynchronous loading of data:


// ------------------------------------------------------------------------
public class MyDataModel : DataItemModel
{

// ----------------------------------------------------------------------
protected override bool DoLoad()
{
// invoke data load
LoadDataAsync( new Action( DataLoaded ) );
return false; // false = continue loading -> asynchronous
} // DoLoad

Page 16 von 30 Version: 1.1.0.0


// ----------------------------------------------------------------------
private void DataLoaded()
{
// apply new data
LoadFinished(); // indicate end of load
} // DataLoaded

} // class MyDataModel

Hierarchical Item Model


For the representation of more complex data structures, hierarchical models are often used. Many
applications use hierarchies to represent functional areas or documents.

The difficulty with the representation of hierarchical structures lies in the required harmonization
of elements with differing representation and runtime behavior in one abstract structure. The
following aspects can be valid for any set of elements in the hierarchy:

• The element has properties which can be edited


• The element can be deleted
• The element can have child elements
• The number of child elements is static, hence it is a pure structural element
• The child elements have to be fetched from some data source
• The child elements should only be (re-) loaded on demand
• A child element can be added

Page 17 von 30 Version: 1.1.0.0


The adaptation of these (relatively abstract) considerations can best be illustrated using some
examples. The first one shows the objects of a .NET application in a hierarchical display:

The following picture shows a possible hierarchical structure of a simple order management:

The Hierarchical Item Model defines a pattern with which UI models can be managed in a
hierarchical way. The elements in this model have all to be derived from the same Item Model. A
mix with other elements is not supported. The Hierarchical Item Model is described by the
interface IHierarchicalItemModel:
// ------------------------------------------------------------------------
public interface IHierarchicalItemModel<out TItemCollection, out TItem> : IItemModel
where TItemCollection : class, ICollection<TItem>, new()
where TItem : IItemModel, IHierarchicalItemModel<TItemCollection,TItem>
{

Page 18 von 30 Version: 1.1.0.0


// ----------------------------------------------------------------------
bool HasChildren { get; }

// ----------------------------------------------------------------------
TItemCollection Children { get; }

// ----------------------------------------------------------------------
TItem Parent { get; }

// ----------------------------------------------------------------------
bool IsRoot { get; }

} // interface IHierarchicalItemModel

The definition governs the relationship between father and child elements. The root element in
the hierarchy has no father element.

As with the Item Model there exist two implementation variants here as well:
Dependency.HierarchicalItemModel and Notifiable.HierarchicalItemModel.

Hierarchical Data Item Model


The Hierarchical Data Item Model combines the patterns Data Item Model and Hierarchical Item
Model and allows the management of hierarchical structures whose elements can be loaded
dynamically:
// ------------------------------------------------------------------------
public interface IHierarchicalDataItemModel<TItem> : IDataItemModel, IHierarchicalItemModel<TItem>
where TItem : IDataItemModel, IHierarchicalDataItemModel<TItem>
{
} // interface IHierarchicalDataItemModel

The usage of the Hierarchical Data Item Model will be described in more detail further on in the
examples Assembly Browser and Order Browser.

The implementation of HierarchicalDataItemModel demonstrates how a class can be developed


for generic usage. According to its declaration, the elements have to support the interface
ILoadable (via IDataModel). To not lose the advantage of the update mechanism provided by Item
Collection, the class checks upon loading whether the element supports IUpdateable. In such a
case, the loading process additionally supports BeginUpdate and EndUpdate.

View Editors

Item Editor
The design pattern Item Editor represents the editing context of an element and offers
corresponding operations. The Item Editor acts as a connecting coordinator between the selected
element and the editing commands. The following implementation is focused on operations of an
LOB object, what amounts to the standard CRUD operations in database terms:
// ------------------------------------------------------------------------
public interface IItemEditor : IItemCreate, IItemEdit, IItemDelete, IItemRefresh,
INotifyPropertyChanged, IDisposable
{

Page 19 von 30 Version: 1.1.0.0


// ----------------------------------------------------------------------
event EventHandler ItemChanged;

// ----------------------------------------------------------------------
object Item { get; set; }

// ----------------------------------------------------------------------
ItemCreateCommand ItemCreateCommand { get; }

// ----------------------------------------------------------------------
ItemEditCommand ItemEditCommand { get; }

// ----------------------------------------------------------------------
ItemDeleteCommand ItemDeleteCommand { get; }

// ----------------------------------------------------------------------
ItemRefreshCommand ItemRefreshCommand { get; }

} // interface IItemEditor

A note in regard to this design:


This implementation variant doesn't restrict its use in terms of the item and uses the type object.
In closed usage scenarios, the Item Editor could be specialized on a certain type:
IItemEditor<TItem> … where TItem : IMyItemType.

The Item Editor provides the editing commands which will be bound to the corresponding controls
in the view:
<Button
Content="New"
Command="{Binding Editor.ItemCreateCommand}" />
<Button
Content="Edit"
Command="{Binding Editor.ItemEditCommand}" />
<Button
Content="Delete"
Command="{Binding Editor.ItemDeleteCommand}" />
<Button
Content="Refresh"
Command="{Binding Editor.ItemRefreshCommand}" />

Which conditions have to be fulfilled for which corresponding actions is determined in a derivation
of class ItemEditor:
// ------------------------------------------------------------------------
public class MyItemEditor : ItemEditor
{

// ----------------------------------------------------------------------
protected override void UpdateCommands()
{
CanCreate =
Item is CompanyCollectionModel ||
Item is CustomerCollectionModel ||
Item is OrderCollectionModel;
CanEdit =
Item is CompanyModel ||
Item is CustomerModel ||
Item is OrderModel;
CanDelete = CanEdit;
CanRefresh = Item is IRefreshable;
} // UpdateCommands

Page 20 von 30 Version: 1.1.0.0


// ----------------------------------------------------------------------
public override void Create( Action onFinished )
{
// create operations

base.Create( onFinished );
} // Create

// ----------------------------------------------------------------------
public override void Edit( Action onFinished )
{
// edit operations

base.Edit( onFinished );
} // Edit

// ----------------------------------------------------------------------
public override void Delete( Action onFinished )
{
// delete operations

base.Delete( onFinished );
} // Delete

} // class MyItemEditor

Item Collection Editor


The Item Collection Editor is a specialization of Item Editor and covers the editing of elements
within an Item Collection. The integrated Item Collection View allows navigation of the associated
Item Collection and thus allows for operations specific to the current position:
// ------------------------------------------------------------------------
public interface IItemCollectionEditor<TItemCollection, TItem> : IItemEditor
where TItemCollection : class, INotifyCollectionChanged, IList<TItem>
{

// ----------------------------------------------------------------------
TItemCollection Items { get; set; }

// ----------------------------------------------------------------------
IItemCollectionView<TItemCollection, TItem> View { get; }

// ----------------------------------------------------------------------
TItem CurrentItem { get; set; }

// ----------------------------------------------------------------------
int CurrentPosition { get; }

// ----------------------------------------------------------------------
void EnsureItem();

} // interface IItemCollectionEditor

The example Customer Admin demonstrates the usage of the Item Collection Editor.

Item Editor Provider


The Item Editor Provider is an Item Editor which dynamically provides a matching editor for the
current element. Based on the .NET object type Type it is possible to register an editor per type.

Page 21 von 30 Version: 1.1.0.0


Such editors based on the type of an element are most applicable in hierarchical structures where
the element type changes dynamically through user interaction and an element type can occur at
several locations within the hierarchy. The interface IItemEditorProvider represents an Item Editor
Provider as follows:
// ------------------------------------------------------------------------
public interface IItemEditorProvider : IItemEditor
{

// ----------------------------------------------------------------------
void RegisterEditor( Type itemType, IItemEditor editor );

// ----------------------------------------------------------------------
void UnregisterEditor( Type itemType );

} // interface IItemEditorProvider

To use an ItemEditorProvider, an editor is registered for every type:


ItemEditorProvider editor = new ItemEditorProvider();
editor.RegisterEditor( typeof( MyModel ), new MyModelEditor() );

MyItemModelEditor itemEditor = new MyItemModelEditor();


editor.RegisterEditor( typeof( MyCollectionModel ), itemEditor );
editor.RegisterEditor( typeof( MyItemModel ), itemEditor );

The example Order Browser demonstrates usage of the Item Editor Provider.

View Presenters

Item Presenter
An Item Presenter describes some object which is responsible for displaying an element. In the UI,
the Item Presenter makes certain that the selection of an element will display a corresponding UI
element for its properties for example. Its representation is covered in the interface
IItemPresenter:
// ------------------------------------------------------------------------
public interface IItemPresenter
{

// ----------------------------------------------------------------------
object BuildContent( object item );

} // interface IItemPresenter

Using the method BuildContent, the Item Presenter creates the UI content for the element.

View Item Presenter


The specialization View Item Presenter is used to determine an Item Presenter, which provides a
view, or more precisely the FrameworkElement, for an element:
// ------------------------------------------------------------------------
public interface IViewItemPresenter<TView> : IItemPresenter
where TView : FrameworkElement, new()
{
} // interface IViewItemPresenter

Page 22 von 30 Version: 1.1.0.0


Item Presenter Provider
The Item Presenter Provider coordinates the available Item Presenters for an element according to
the type:
// ------------------------------------------------------------------------
public interface IItemPresenterProvider : INotifyPropertyChanged, IDisposable
{

// ----------------------------------------------------------------------
object Item { get; set; }

// ----------------------------------------------------------------------
IEnumerable ItemContent { get; }

// ----------------------------------------------------------------------
IItemPresenter DefaultPresenter { get; set; }

// ----------------------------------------------------------------------
void RegisterPresenter( Type itemType, IItemPresenter presenter );

// ----------------------------------------------------------------------
void UnregisterPresenter( Type itemType, IItemPresenter presenter );

} // interface IItemPresenterProvider

For each element type an IItemPresenter can be registered. Upon each change of the Item, the
contents of the available Item Presenters will be made available in the property ItemContent.
Through the property DefaultPresenter it is possible to select an Item Presenter for unknown
types.

The example Order Browser demonstrates the usage of the Item Presenter Provider.

Examples

Media Player
By means of a media player it is demonstrated how the control MediaPlayerController builds a
bridge between the existing controls MediaPlayer and Button to control interaction with Action
and Command.

The MediaPlayerController controls a MediaPlayer and has no visual representation of its own.
The property ElementName connects the MediaPlayerController with the MediaPlayer:
<MediaElement
x:Name="MediaElement"
Grid.Row="0"
LoadedBehavior="Manual"
Source="{Binding Source}" />

<XamlControls:MediaPlayerController
x:Name="MediaPlayerController"
MediaElement="{Binding ElementName=MediaElement}" />

The control of the MediaPlayer happens through buttons which are bound to the commands of
the MediaPlayerControllers:
<Button

Page 23 von 30 Version: 1.1.0.0


Content="Play"
ToolTipService.ToolTip="Play Movie"
Visibility="{Binding CanPlay, ElementName=MediaPlayerController,
Converter={StaticResource FlagToVisibleConverter}}"
Command="{Binding PlayCommand, ElementName=MediaPlayerController}" />

The various commands of the media player Play/Pause/Resume/Stop are held in a


CommandCollection and will be released upon disposal of the MediaPlayerController.

Further on, the MediaPlayerController demonstrates how to harmonize the discrepancies between
the control versions of MediaPlayer for WPF and Silverlight/WP7.

Collection Binding
The examples ListBox Binding and GridView Binding demonstrate the binding of lists to the
corresponding UI element. Their primary use is to measure loading performance (see chapter Item
Collection). The following runtime aspects can be adapted:

• The kind of the data source: IItemCollection or ICollectionView (only for WPF ListBox)
• The type of binding to the Item Model: DependencyProperty or INotifyPropertyChanged
• The number of items
• The update mode – with or without support for IUpdateable
The example GridView Binding is only available for WPF and Silverlight.

Selection
The example Selection uses the controls ListBox and DataGrid to demonstrate how the pattern
Item Collection Selection can be used to handle multiple selections.

Connection of the controls to the ItemCollectionSelection happens through the helper elements
SelectorSelection (ListBox, ComboBox) as well as DataGridSelection (DataGrid):
<ListBox
SelectionMode="Extended"
ItemsSource="{Binding Customers}"
Controls:SelectorSelection.Selection="{Binding CustomerSelection}" />

<DataGrid
SelectionMode="Extended"
ItemsSource="{Binding Customers}"
Controls:DataGridSelection.Selection="{Binding CustomerSelection}" />

Customer Admin
The example Customer Admin demonstrates how the pattern Item Collection Editor can be applied
to a DataGrid. It offers the following functionality:

• Register a new customer


• Determine the insert position of new customers
• The selection behavior upon insertion of a new customer

Page 24 von 30 Version: 1.1.0.0


• Modify a customer
• Delete a customer (with deletion confirmation)
• Support for keyboard input (Insert, F2 and Delete)
• Support for mouse double clicks
To properly support the desired functionality, the derivation ModelDataGrid of DataGrid was
written. Aside from the Commands ItemCreate, ItemEdit and ItemDelete, ModelDataGrid provides
the necessary support for keyboard input and handling of mouse clicks (tool MouseClickManager).

The editing functionality for customers is implemented in the class CustomerEditor. The
CustomerAdminModel combines the information about a customer and the editor and acts as a
view model for the view:
// ------------------------------------------------------------------------
public class CustomerAdminModel : ItemModel
{

// ----------------------------------------------------------------------
public CustomerAdminModel( ViewSyncMode syncMode )
{
RegisterDisposable( customers );
editor = new CustomerAdminEditor( customers, syncMode );
Load();
} // CustomerAdminModel

// ----------------------------------------------------------------------
public ItemCollection<CustomerModel> Customers
{
get { return customers; }
} // Customers

// ----------------------------------------------------------------------
public IItemEditor Editor
{
get { return editor; }
} // Editor

// ----------------------------------------------------------------------
private void Load()
{
} // Load

// ----------------------------------------------------------------------
// members
private readonly ItemCollection<CustomerModel> customers = new ItemCollection<CustomerModel();
private readonly CustomerAdminEditor editor;

} // class CustomerAdminModel

After the CustomerAdminModel has been assigned to the view as a DataContext, the view can
make use of the elements as follows:
<Button
Content="New"
Command="{Binding Editor.ItemCreateCommand}" />

<XamlControls:ModelDataGrid
...
IsSynchronizedWithCurrentItem="True"

Page 25 von 30 Version: 1.1.0.0


ItemsSource="{Binding Customers}"
SelectedItem="{Binding Editor.Item, Mode=TwoWay}"
ItemCreateCommand="{Binding Editor.ItemCreateCommand}" />
...
</XamlControls:ModelDataGrid>

Assembly Browser
The Assembly Browser lists the assemblies of the application in a hierarchy and uses the
Hierarchical Data Item Model for this. The base class ReflectionModel with its various derivations
acts as the basis for the hierarchical structure:

The class HierarchicalDataItemModel serves as base class for ReflectionModel, using a list
declaration of ItemCollection<ReflectionModel>, which leads to the optimization of IUpdateable to
be used for the loading of all lists.

The presentation of the assembly data happens in a TreeView. The TreeView control offers no
support for on demand loading (lazy load) of its data and thus is made capable of this by the
derivation ModeTreeView. Based on the action ILoadable, the classes ModeTreeView and
ModelTreeViewItem offer the corresponding support. The required data will be loaded during
construction of the TreeView elements (PrepareContainerForItemOverride). More information on
this approach can be found in Silverlight TreeView Advanced Scenarios.

Additionally, the control ModeTreeView offers commands to expand and collapse a branch, which
can be bound to any ICommand enabled control in the view:
<Button

Page 26 von 30 Version: 1.1.0.0


Content="Expand"
Command="{Binding ExpandCommand, ElementName=AssembliesTree}" />
<Button
Content="Collapse"
Command="{Binding CollapseCommand, ElementName=AssembliesTree}" />

<XamlControls:ModelTreeView
x:Name="AssembliesTree"
ItemsSource="{Binding Assemblies.Children}" />

Order Browser
The Order Browser example is used to demonstrate how a hierarchical structure can be used to
manage the following objects:

• Management of companies
• Management of customers of a company
• Management of orders of a company
• Display of order items of an order
To represent the view models, the pattern Hierarchical Data Item Model is used with the base
class OrderModelBase:

Representation of a company is implemented in the class CompanyModel, which - aside from the
company data - acts as a pure structure element for the customers and orders:
// ----------------------------------------------------------------------
protected override bool DoLoad()
{
Children.Add( new CustomerCollectionModel( this ) );
Children.Add( new OrderCollectionModel( this ) );
return true;
} // DoLoad

The customers, represented by the class CustomerModel, will be loaded on demand from a data
source by the container class CustomerCollectionModel:
// ----------------------------------------------------------------------

Page 27 von 30 Version: 1.1.0.0


protected override bool DoLoad()
{
ILIst<ICustomer> customers = LoadCustomers(); // load from your data source
foreach ( ICustomer customer in customers )
{
Children.Add( new CustomerModel( this, customer.FirstName, customer.LastName, customer.Address )
);
}
return true;
} // DoLoad

The base class HierarchicalDataItemModel considers IUpdateable and executes the method
DoLoad within BeginUpdate and EndUpdate.

Editing of elements is defined by the Item Editor Provider in the view model OrderBrowserModel
of the view:
// ------------------------------------------------------------------------
public class OrderBrowserModel : OrderModelBase
{

// ----------------------------------------------------------------------
public OrderBrowserModel()
{
// companies
companies = new CompanyCollectionModel( this );
Children.Add( companies );

// editor
RegisterDisposable( editor );
editor.ItemChanged += ItemChanged;
editor.RegisterEditor( typeof( CompanyCollectionModel ), new CompanyCollectionEditor() );
editor.RegisterEditor( typeof( CompanyModel ), new CompanyEditor() );

CustomerEditor customerEditor = new CustomerEditor();


editor.RegisterEditor( typeof( CustomerCollectionModel ), customerEditor );
editor.RegisterEditor( typeof( CustomerModel ), customerEditor );

OrderEditor orderEditor = new OrderEditor();


editor.RegisterEditor( typeof( OrderCollectionModel ), orderEditor );
editor.RegisterEditor( typeof( OrderModel ), orderEditor );

// presenter
RegisterDisposable( presenter );
presenter.RegisterPresenter( typeof( CompanyModel ), new ViewItemPresenter<CompanyInfoView>() );
presenter.RegisterPresenter( typeof( CustomerModel ), new ViewItemPresenter<CustomerInfoView>()
);
presenter.RegisterPresenter( typeof( OrderModel ), new ViewItemPresenter<OrderInfoView>() );
presenter.RegisterPresenter( typeof( OrderModel ), new ViewItemPresenter<OrderItemsInfoView>()
);
} // OrderBrowserModel

// ----------------------------------------------------------------------
public override string Name
{
get { return "Companies"; }
} // Name

// ----------------------------------------------------------------------
public CompanyCollectionModel Companies
{
get { return companies; }
} // Customers

Page 28 von 30 Version: 1.1.0.0


// ----------------------------------------------------------------------
public IItemEditor Editor
{
get { return editor; }
} // Editor

// ----------------------------------------------------------------------
public IItemPresenterProvider Presenter
{
get { return presenter; }
} // Presenter

// ----------------------------------------------------------------------
private void ItemChanged( object sender, EventArgs e )
{
presenter.Item = editor.Item;
} // ItemChanged

// ----------------------------------------------------------------------
// members
private readonly CompanyCollectionModel companies;
private readonly ItemEditorProvider editor = new ItemEditorProvider();
private readonly IItemPresenterProvider presenter = new ItemPresenterProvider();

} // class OrderBrowserModel

The editors registered in the ItemEditorProvider can be used for a single type or combined for
multiple types. This is demonstrated in the example by the CustomerEditor which is used both for
the types CustomerCollectionModel and CustomerModel.

To ensure that the functionality of the editors can be used in combination with the TreeView, the
derivation ModelTreeView (also see example Assembly Browser) allows the binding to the
Commands ItemCreate, ItemEdit and ItemDelete.

For the element types, corresponding Item Presenters are registered in the ItemPresenterProvider.
The order demonstrates how several View Item Providers can be used for an element. Through
ItemChanged, the active element of the ItemEditorProvider is synchronized with the
ItemPresenterProvider.

Usage of the view happens as follows:


<Button
Content="New"
Command="{Binding Editor.ItemCreateCommand}" />
<Button
Content="Edit"
Command="{Binding Editor.ItemEditCommand}" />
<Button
Content="Delete"
Command="{Binding Editor.ItemDeleteCommand}" />
<Button
Content="Refresh"
Command="{Binding Editor.ItemRefreshCommand}" />

<XamlControls:ModelTreeView

ItemsSource="{Binding Children}"
ItemTemplate="{StaticResource NameTemplate}"
SelectedItem="{Binding Editor.Item, Mode=TwoWay}"

Page 29 von 30 Version: 1.1.0.0


ItemCreateCommand="{Binding Editor.ItemCreateCommand}"
ItemEditCommand="{Binding Editor.ItemEditCommand}"
ItemDeleteCommand="{Binding Editor.ItemDeleteCommand}"/>

<ItemsControl
ItemsSource="{Binding Presenter.ItemContent}"/>

Tools and Environment

Converter
For controlling dependencies between controls, the following base converters are used:

ObjectToVisibleConverter Changes the visibility of the target element to Visibility.Visible in


case the source is not undefined.

ObjectToCollapsedConverter Changes the visibility of the target element to Visibility.Collapsed in


case the source is not undefined.

FlagToVisibleConverter Changes the visibility of the target element to Visibility.Visible in


case the source is a Boolean with the value true.

FlagToCollapsedConverter Changes the visibility of the target element to Visibility.Collapsed in


case the source is a Boolean with the value true.

UriToImageConverter Converts an Uri into a BitmapImage.

Composite Library Development


The article Time Period Library for .NET gives an in depth description of the development of
Composite Libraries.

In composite development of Silverlight and Windows Phone 7, the debugger of the Windows
Phone emulator doesn't consider the setting for the temporary build files. This can be corrected
with the following Post-build event:
xcopy "$(ProjectDir)obj\WindowsPhone.$(ConfigurationName)\$(ConfigurationName)\XapCacheFile.xml"
"$(ProjectDir)obj\$(ConfigurationName)\XapCacheFile.xml" /Y

Page 30 von 30 Version: 1.1.0.0

You might also like