Presentation Patterns For XAML Based Applications
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.
The design patterns can be viewed isolated and can be put to use in an existing architecture.
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:
Data • Clearable
• Loadable
• Refreshable
• Resettable
• Selectable
• Sortable
• Comparer Sortable
• Comparison Sortable
• Updateable
Media • Play
• Pause
• Resume
• Stop
Navigation • Expandable
• Collapsible
// ----------------------------------------------------------------------
bool IsExpandable { get; }
// ----------------------------------------------------------------------
void Expand();
} // interface 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:
• 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.
// ----------------------------------------------------------------------
event EventHandler Started;
// ----------------------------------------------------------------------
event EventHandler Finished;
// ----------------------------------------------------------------------
string Name { get; }
// ----------------------------------------------------------------------
bool IsExecuting { get; }
// ----------------------------------------------------------------------
void Refresh( object parameter );
} // interface ICommand
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
// ----------------------------------------------------------------------
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:
// ----------------------------------------------------------------------
void AddAll( IEnumerable<TItem> items );
// ----------------------------------------------------------------------
void Replace( TItem oldItem, TItem newItem );
} // interface IItemCollection
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:
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" ) );
• 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 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
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.
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;
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
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
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
// ----------------------------------------------------------------------
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:
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:
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
// ----------------------------------------------------------------------
// members
private readonly RefreshCommand refreshCommand;
} // class MyModel
// ----------------------------------------------------------------------
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.
// ----------------------------------------------------------------------
protected override bool DoLoad()
{
// data load
LoadDataSync();
return true; // true = load finished -> synchronous
} // DoLoad
} // class MyModel
// ----------------------------------------------------------------------
protected override bool DoLoad()
{
// invoke data load
LoadDataAsync( new Action( DataLoaded ) );
return false; // false = continue loading -> asynchronous
} // DoLoad
} // class MyDataModel
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 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>
{
// ----------------------------------------------------------------------
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.
The usage of the Hierarchical Data Item Model will be described in more detail further on in the
examples Assembly Browser and Order Browser.
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
{
// ----------------------------------------------------------------------
object Item { get; set; }
// ----------------------------------------------------------------------
ItemCreateCommand ItemCreateCommand { get; }
// ----------------------------------------------------------------------
ItemEditCommand ItemEditCommand { get; }
// ----------------------------------------------------------------------
ItemDeleteCommand ItemDeleteCommand { get; }
// ----------------------------------------------------------------------
ItemRefreshCommand ItemRefreshCommand { get; }
} // interface IItemEditor
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
// ----------------------------------------------------------------------
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
// ----------------------------------------------------------------------
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.
// ----------------------------------------------------------------------
void RegisterEditor( Type itemType, IItemEditor editor );
// ----------------------------------------------------------------------
void UnregisterEditor( Type itemType );
} // interface IItemEditorProvider
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.
// ----------------------------------------------------------------------
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
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:
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"
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
<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:
// ----------------------------------------------------------------------
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() );
// 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
// ----------------------------------------------------------------------
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.
<XamlControls:ModelTreeView
…
ItemsSource="{Binding Children}"
ItemTemplate="{StaticResource NameTemplate}"
SelectedItem="{Binding Editor.Item, Mode=TwoWay}"
<ItemsControl
ItemsSource="{Binding Presenter.ItemContent}"/>
Converter
For controlling dependencies between controls, the following base converters are used:
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