Silverlight Playground
about Silverlight and other Amenities

A generic ViewModel to publish sets of objects

2009-12-16T22:14:18+01:00 by Andrea Boschin

The programming with the MVVM pattern often requires a proliferation of types because there are more Views that need a ViewModel than you might think. You can think that you need to associate a ViewModel only to simple blocks of your user interface, but while you are writing code you often understand that there are other cases where a ViewModel is required.

As an example while populating a DataGrid binding simple entities to the grid is not  good idea. A single row of a grid not only contains some data to be displayed but often may contain some commands, a link to be clicked, a button to delete an item and so on.

All these are cases where you need a ViewModel to correcty handle the binding and the related commands. So you have to create a new type, add a bunch of properties and some commands and finally populate the grid with instances of the new type. Then you will find someway difficult to route the command from the child ViewModel to the one that has generated it.

Simple is better.

In my recent applications I found a reasonable workaround that correctly handle the situation but avoid a proliferation of classes and as usual in this cases the solution comes from a generic type. The trick comes from the understanding that often in this scenario you have some properties of an entity to be binded and one or two commands to be raised. And in the most cases the command have to be handled by a parent ViewModel. Imagine to have a collection which is shown in a DataGrid and a delete command on each row; To remove the row, the command has to be catched by the ViewModel which is exposing the collection. So I decided to create a simple class named RowViewModel this way:

   1: public class RowViewModel<T,K> : ViewModel
   2:     where T : ViewModel
   3: {
   4:     /// <summary>
   5:     /// Initializes a new instance of the RowViewModel class.
   6:     /// </summary>
   7:     /// <param name="parent">The parent.</param>
   8:     /// <param name="payload">The payload.</param>
   9:     public RowViewModel(T parent, K payload)
  10:     {
  11:         this.Parent = parent;
  12:         this.PayLoad = payload;
  13:     }
  14:  
  15:     /// <summary>
  16:     /// Gets or sets the parent.
  17:     /// </summary>
  18:     /// <value>The parent.</value>
  19:     public T Parent
  20:     {
  21:         get { return this.GetValue<T>("Parent"); }
  22:         set { this.SetValue<T>("Parent", value); }
  23:     }
  24:  
  25:     /// <summary>
  26:     /// Gets or sets the pay load.
  27:     /// </summary>
  28:     /// <value>The pay load.</value>
  29:     public K PayLoad
  30:     {
  31:         get { return this.GetValue<K>("PayLoad"); }
  32:         set { this.SetValue<K>("PayLoad", value); }
  33:     }
  34: }

To simplify the creation of the collection property I've created a class RowViewModelCollection that inherits from the ObservableCollection. This allow me to declare the observable properties with a less verbose syntax:

   1: public class RowViewModelCollection<T, K> : ObservableCollection<RowViewModel<T, K>>
   2:     where T : ViewModel
   3: { }

Now we are ready to publish a DataGrid using this class. The RowViewModel exposes T as the parent ViewModel and K as the payload. This is the entity we want to display in the grid. So we can write a XAML similar to this:

   1: <data:DataGrid ItemsSource="{Binding Tasks}">
   2:     <data:DataGrid.Columns>
   3:         <data:DataGridTemplateColumn>
   4:             <data:DataGridTemplateColumn.CellTemplate>
   5:                 <DataTemplate>
   6:                     <HyperlinkButton cmd:Click.Command="{Binding Parent.DeleteTaskCommand}"
   7:                                      cmd:Click.CommandParameter="{Binding PayLoad}"
   8:                                      HorizontalAlignment="Center" VerticalAlignment="Center" >
   9:                         <Image Height="16" HorizontalAlignment="Center" VerticalAlignment="Center" Width="16" Source="/Elite.TimeTracker.Client;Component/Images/delete.png" Stretch="Fill"/>
  10:                     </HyperlinkButton>
  11:                 </DataTemplate>
  12:             </data:DataGridTemplateColumn.CellTemplate>
  13:         </data:DataGridTemplateColumn>
  14:         <data:DataGridTextColumn Header="Cliente" Binding="{Binding PayLoad.Customer.Description}" />
  15:         <data:DataGridTextColumn Header="Ora" Binding="{Binding PayLoad.StartTime}" />
  16:         <data:DataGridTextColumn Header="Descrizione" Binding="{Binding PayLoad.Description, Converter={StaticResource ellipsis}}" />
  17:         <data:DataGridCheckBoxColumn Header="Chiam." Binding="{Binding PayLoad.IsPhoneCall}" />
  18:         <data:DataGridCheckBoxColumn Header="Compl." Binding="{Binding PayLoad.IsCompleted}" />
  19:     </data:DataGrid.Columns>
  20: </data:DataGrid>

In a couple of words we have:

ItemsSource="{Binding Tasks}" - This connect the DataGrid to a RowViewModelCollection declared in the parent ViewModel.

Binding="{Binding PayLoad.StartTime}" - This take advantage of the dot syntax of the DataBinding that allow to access complex properties of the binded objects. Remember that PayLoad is the instance of the entity to bind to the DataGrid so PayLoad.StartTime binds the StartTime property to the cell.

cmd:Click.Command="{Binding Parent.DeleteTaskCommand}" - This binds the click event of the HyperlinkButton to a command of the parent ViewModel.

cmd:Click.CommandParameter="{Binding PayLoad}" - This binding let me pass the entire entity the delegate command.

This tecnique simplifies the code required to handle the DataGrid. When a command is raised the parent ViewModel handle it and this reduce the need to use an EventBroker, and it gives a more simple and maintainable code.

Is this good only for DataGrids?

There are a lot of cases where you have to bind a collection to a control, the ItemsControl, the ListBox, and so on. Every time you encounter one of these cases you can obviously apply this tecnique. Of course it is useful only if you need to handle some commands, otherwise you can simply bind the entity directly to the control.
In the code I've attached to a my previous post - the -you can find a working sample of the workaround. 

Categories:   Databinding | TIPS
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Use CollectionViewSource effectively in MVVM applications

2009-07-18T23:13:15+01:00 by Andrea Boschin

In a my I've outlined the new CollectionViewSource introduced in silverlight with the latest release. This control is really useful while we need to sort and filter collection of objects in memory, but if we need to use a Model-View-ViewModel pattern to develop our application there is some problems using the control.

To implement the MVVM pattern we need to inject a ViewModel into the View using the DataContext property. This is usual in this kind of application because we need to bind the properties of the ViewModel to the controls in the View. So if we need to use a CollectionViewSource we can try to bind a collection from the ViewModel to the CVS and then bind the control to the CVS itself. Unfortunately this approach does not work because we cannot bind the CVS directly to the DataContext of the page. Trying to do this operation we will get a AG_E_BAD_PROPERTY_VALUE exception because the control does not inherits from FrameworkElement so it does not support DataBinding.

However, if it were possible, the CollectionViewSource would force us to put a lot of code in the codebehind to handle the Filter event and the sorting property. So, we have to follow another way to use the CollectionViewSource effectively with this kind of applications.

How to use the CollectionViewSource

The only way to use the CollectionViewSource with the MVVP pattern is to expose it as the type of the bindable property. This way we can manage the properties of the CVS and handle the Filter event from inside the ViewModel without putting anything in the codebehind of the View.

First of all we have to create two properties in the ViewModel. The first property will be the ObservableCollection<T> and we will populate it with the data to display. This property can be private because it will be used only to feed the ColectionViewSource of the second property. Here is how to initialize the properties:

   1: /// <summary>
   2: /// Gets or sets the names.
   3: /// </summary>
   4: /// <value>The names.</value>
   5: public CollectionViewSource Names { get; set; }
   6:  
   7: /// <summary>
   8: /// Gets or sets the names internal.
   9: /// </summary>
  10: /// <value>The names internal.</value>
  11: private ObservableCollection<string> NamesInternal { get; set; }
  12:  
  13: /// <summary>
  14: /// Initializes a new instance of the <see cref="MainPageViewModel"/> class.
  15: /// </summary>
  16: public MainPageViewModel()
  17: {
  18:     this.NamesInternal = new ObservableCollection<string>();
  19:     this.Names = new CollectionViewSource();
  20:     this.Names.Source = this.NamesInternal;
  21:  
  22:     // populate the ObservableCollection
  23:     foreach (string item in DataSource.GetNames())
  24:         this.NamesInternal.Add(item);
  25: }

Now, to bind the CollectionViewSource in the XAML we have to rely on his View property. If we put a reference to the CVS in the property ItemsSource in the XAML it simply will not work. Here is how to correctly bind:

   1: <ListBox ItemsSource="{Binding Names.View}" Margin="5,5,5,1" Grid.ColumnSpan="4" />

Once we have connected the CVS to the collection and the UI to the CVS we can handle the commands from the View and interact with the CVS to modify how to display the data. In the sample I have a togglebutton that is raising a ChangeSortDirectionCommand. I use the DelegateCommand<T> from Prism V2.0 and when someone send the command I change the SortDescriptor in the CollectionViewSource.

In the same View the TextChanged event is raised when the user type in the textbox. This command handled by the ViewModel let the CVS refresh itself. This way the Filter event will be evaluated again and the View will be updated with the filter applied.

I've attached to the post a new version of the sample I've proposed with the first article. Now the sample use the MVVM pattern with the CollectionViewSource.

Download: Elite.Silverlight3.CollectionViewMVVM.zip (~520 KB)

Categories:   Databinding | Prism
Actions:   E-mail | del.icio.us | Permalink | Comments (3) | Comment RSSRSS comment feed

Silverlight 3.0 RTW: The CollectionViewSource

2009-07-10T17:45:00+01:00 by Andrea Boschin

Since the first release of WPF the framework contains a component called CollectionViewSource. The CollectionViewSource allow developer to apply sorting, filtering and grouping to in-memory collections of objects. This component is very useful when small collections (up to hundreds of items) is loaded in memory and need to give a better experience to the user.

Some months ago I of the CollectionViewSource for Silverlight 2.0. I worked hard to implement the component and finally I published a version with the only limitation of not allowing grouping. While sorting and filtering are implemented directly on the data seamless, the grouping require an infrastructure to be consumed (grouping support in ItemsControl) that I was unable to provide.

The new release of Silverlight 3.0 RTW comes with an implementation of this component that fill this hole but it has the same limitations of my CollectionViewSource. This limitations come from the same reason I cannot implement by myself: the missing grouping support in ItemsControls. However the native component has a some advantages on mine, first of all a very strict compatibility with the architecture of the WPF CollectionViewSource. In this post I will show you how to configure and use the component in a Silverlight application.

Using the CollectionViewSource

The Silverlight CollectionViewSource is a class that inherits from DependencyObject and usually can be inserted in a resource dictionary in the page where it has to work. The principle of the CVS (this is the shorter name I will use from this point in advace) is acting as a filter between the source collection (IEnumerable or IList) and the component where we are showing the data. While binding a ListBox directly to a flat collection will show all the instances in the collection, putting the CVS in the middle enable us to operate on its properties to sort and filter the source.

Here is an example that shows how to embed the CVS in a xaml page; The first part show a DataSource object SampleDataCollection in the UserControl.Resources, and a CollectionViewSource that reference it using StaticResource. In the second part there is a ListBox that reference the CVS also using a StaticResource markup extension.

   1: ... omissis ...
   2:  
   3: <UserControl.Resources>
   4:     <local:DataSource x:Key="dataSource" />
   5:     <CollectionViewSource x:Name="cvs" Source="{Binding Names, Source={StaticResource dataSource}}">
   6:         <CollectionViewSource.SortDescriptions>
   7:             <scm:SortDescription Direction="Ascending" />
   8:         </CollectionViewSource.SortDescriptions>
   9:     </CollectionViewSource>
  10: </UserControl.Resources>
  11:  
  12: ... omissis ...
  13:  
  14: <ListBox ItemsSource="{Binding Source={StaticResource cvs}}" 
  15:          Margin="5,5,5,1" Grid.ColumnSpan="4" />
  16:  
  17: ... omissis ...

The magic around CVS come from an interface called ICollectionView that is esposed by the CVS in the View property. This interface represent the real working object that behind the scenes understand the source type and manage to apply filtering and sorting seamless. When a datasource is connected to the CVS a specialized instance of the ICollectionView is created - EnumerableCollectionView for IEnumerable or ListCollectionView for IList - and this instance work knowing the source data. When we connect a consumer control to the CVS it is binded directly to the ICollectionView. Watching at this interface we will see it implements INotifyCollectionChanged. This mean that if we connect the CVS to an ObservableCollection<T> all the updates to the source collection will be transmitted to the consumer.

Sorting and Filtering

Now after the basics of the CVS it is time to try to use its properties to apply the features it implements. If we see at the CVS we will see a Filter event exposed to the markup or to the code. This event is raised by the CVS every time it need to understand if an item of the collection is valid for the filter the user is appling. In the semple I included at the end of this post I use the CVS to filter a collection of italian names (strings). In the Filter event I simply compare every item in the collection to the letters the use typed in a TextBox:

   1: /// <summary>
   2: /// Handles the Filter event of the cvs control.
   3: /// </summary>
   4: /// <param name="sender">The source of the event.</param>
   5: /// <param name="e">The <see cref="System.Windows.Data.FilterEventArgs"/> instance containing the event data.</param>
   6: void cvs_Filter(object sender, FilterEventArgs e)
   7: {
   8:     e.Accepted = ((string)e.Item).ToLower().StartsWith(searchKey.Text.ToLower());
   9: }

The FilterEventArgs class expose an Accepted property useful to indicate if the element (e.Item) we are evaluating has to be shown or not. This event is very simple to handle and let the developer to apply various filtering rules. We have only to pay attention to the time that the evaluation take because it is repeated one time for every element in the collection.

While the user type in the TextBox we catch the TextChanged event and force the refresh of the CVS this way:

   1: /// <summary>
   2: /// Handles the TextChanged event of the searchKey control.
   3: /// </summary>
   4: /// <param name="sender">The source of the event.</param>
   5: /// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param>
   6: private void searchKey_TextChanged(object sender, TextChangedEventArgs e)
   7: {
   8:     this.cvs.View.Refresh();
   9: }

Applying sorting if simpler than filtering. It is done by using markup with the SortDescriptions property. This property accept a collection of SortDescription, one for every property we need to sort. For each property we can choice the direction of sorting using the Direction property:

<scm:SortDescription Direction="Ascending" PropertyName="Name" />

Here is a snippet of code from the sample i provide:

   1: <CollectionViewSource x:Name="cvs">
   2:     <CollectionViewSource.SortDescriptions>
   3:         <scm:SortDescription Direction="Ascending" />
   4:     </CollectionViewSource.SortDescriptions>
   5: </CollectionViewSource>

While I'm binding strings there is no need to specify a property because the sort has to be applied directly to the source string. If you need a more complex sorting logic it is always available the IComparable interface that is used to evaluate the elements during the sort operation.

Differences from WPF

While at the first look the CollectionViewSource implementation in Silverlight appear to be very close to the WPF component, there is some subtle differences. First of all if we examine the component we may found a property called GroupDescriptions. This property is used in WPF to apply grouping but in the Silverlight version it throw a NotSupportedException. Probably it is a placeholder for future extensions of the component.

The most important difference to me if about the structure of the object. The CVS inherits from DependencyObject but while in silverlight only classes derived from FrameworkElement may be subject of DataBinding the only way to put a CVS in markup would be using a StaticResource.

This would be a huge limitation to CVS but fortunately the people from the team found a way to workaround it. Also if CVS inherits from DependencyObject it is still possible to use databinding but it is needed to specify an explicit Source like the following example:

   1: <UserControl.Resources>
   2:     <CollectionViewSource x:Name="cvs" Source="{Binding Names, Source={StaticResource dataSource}}">
   3:         <CollectionViewSource.SortDescriptions>
   4:             <scm:SortDescription Direction="Ascending" />
   5:         </CollectionViewSource.SortDescriptions>
   6:     </CollectionViewSource>
   7: </UserControl.Resources>

This is very useful if we have a complex object like a ViewModel we want to use in a page with a CVS instance. The better would be to bind directly to the datasource but is is possible to put the ViewModel in the resources and then statically reference it from both the DataContext and the CollectionViewSource.

Conclusion

Last months I decided to start writing a component like CVS because I think it is really useful in some cases. Now I'm very glad to rely on a framework component. For some of you that have been used my component take note that the CVS interface is very close to my component so it is very easy to switch to it.

Download: Elite.Silverlight3.CollectionViewSample.zip (1,04 MB)

Video: CollectionViewSource.wmv (1.08 MB)

Categories:   Databinding | News
Actions:   E-mail | del.icio.us | Permalink | Comments (6) | Comment RSSRSS comment feed

Improving Silverlight 2.0 databinding with a CollectionViewSource control

2009-03-16T23:59:00+01:00 by Andrea Boschin

CollectionViewSource screenshot People that have worked with Windows Presentation Foundation probably know the usefulness of the CollectionViewSource control. This control is simple to be configured and enables to easily filter, sort and group data items before binding them to an ItemsControl, without any need to change the underlying datasource. Many times we have to operate filtering on a datasource before displaying it in a listbox, or we need to sort the same data answering to a user request. Both this behaviors may be handled using the CollectionViewSource; filtering will raise an event asking to choice the items to show or to hide and we have simply to select the elements with the correct criteria. On the other side, the control can sort elements on one or more properties ascending or descending. If the target control support grouping we can also specify a grouping criteria and the control will operate seamless for us. But probably the better thing is that we can change sorting, filtering or grouping and the target control will be updated according to the new criteria.

Unfortunately Silverlight 2.0 does not have a CollectionViewSource but while exploring the capabilities of databinding I found that it is possible to build a control similar to the CollectionViewSource that support both filtering and sorting. There aren't controls supporting grouping in Silverlight 2.0 so it is unuseful to implement it in this control. Before getting to know how the control works it is better to understand how databinding works in Silverlight 2.0 so let me begin with a brief introduction to this argument.

Databinding in Silverlight 2.0

Databinding is a feature introduced many years ago so there is no need to explain what it is. Windows Presentation Foundation has taken this ancient tecnique to an high level of simplicity and Silverlight 2.0 implements a databinding capability very close to WPF with some little limitations.

The two main components of databinding are DataContext and the Binding markup extension. DataContext is a property, exposed by all the classes inheriting from FrameworkElement so it is available on all the controls, shapes, panels and other user interface elements. Through DataContext property we may specify a class instance that will be the source of databinding for the target element and for all his children. The DataContext will propagate to children elements until the end of the hierarchy or until another DataContext will be encountered. All the elements of the hierarchy may be binded with a property of the object that is associated to the DataContext. To bind the properties each other we have to use the Binding markup extension. In the simplest form the markup extension require that we specify the property of the datasource to bind to.

Another way to bind to a user interface control is using an ItemsControl. Connecting a datasource to its ItemsSource property will cause the collection to be displayed in the control repeating a DataTemplate for every item in the collection. The DataTemplate will be binded to each element throught the Binding markup extension itself. Here is an example showing both the tecniques applied.

   1: <UserControl x:Class="Elite.Silverlight.Samples.Databinding"
   2:     xmlns="http://schemas.microsoft.com/client/2007" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:d="clr-namespace:Elite.Silverlight.Samples"
   5:     Width="400" Height="300">
   6:     <UserControl.Resources>
   7:         <d:DataSource x:Key="data" />
   8:     </UserControl.Resources>
   9:     <Grid x:Name="LayoutRoot" DataContext="{StaticResource data}" Background="White">
  10:         <ItemsControl ItemsSource="{Binding Strings}">
  11:             <ItemsPanelTemplate>
  12:                 <DataTemplate>
  13:                     <TextBlock Text="{Binding}" />
  14:                 </DataTemplate>
  15:             </ItemsPanelTemplate>
  16:         </ItemsControl>
  17:     </Grid>
  18: </UserControl>

In this sample an instance of a class named DataSource has been referenced in the user control resources. This class has a property named "Strings" returning an IEnumerable<string>. The yielded strings is the data to be displayed in the ItemsControl. The DataContext property of the Grid is assigned to a reference of the DataSource class and the ItemsSource property is binded to the Strings property throught the Binding markup extension. The data collected from the datasource become the DataContext for the each item with the template specified in ItemsTemplatePanel so the Binding markup extension assign each string to the Text property of the TextBlock.

For the purpose of this article we don't need to deepen our knowledge of this aspect. Instead, we need to concentrate on another aspect. Databinding enable developers to bind properties of a control to properties of a data item and have them updated when the data item change. In the same way it is possible to bind a collection to a specific property and have the control updated when we add or remove items to the collection. This two behaviors came from two interfaces: INotifyPropertyChanged and INotifyCollectionChanged. Objects implementing one of these interfaces have to notify internal changes through an event. PropertyChanged notify changes to single properties and is useful for objects binded directly to control properties, instead CollectionChanged notify about adding or removal of items from a collection and it has to be used for controls binded to collections like an ItemsControl.

Silverlight 2.0 and WPF come with a generic collection ObservableCollection<T> that implements INotifyCollectionChanged interface. This class helps us to bind collection of objects to an ItemsControl. We may create a class, expose a property of this type and bind it to an ItemsControl. Then we can add or remove elements to the collection and them will be added to or removed from the ItemsControl seamless.

Thinking about our CollectionViewSource

It is now the time to think about how to implement our CollectionViewSource. We need to create the control as a filter that will be interposed between the datasource and the consuming control and it will operate notifying changes responding to user choices. So probably our control has to appear as a collection for  the control that will displays the data items. To notify changes we need to implement the INotifyCollectionChanged interface so we get the consuming control to update its appearance responding to changes to the filtering or sorting criteria. Watching to the control from the side of the datasource it has to expose a Source property that we'll connect to the underlying datasource we need to transform.

So let start creating the control. It will be called CollectionViewSource as expected and extends the Control class. We cannot create a simple class that does not implement any other base class because we need to get access to the DependencyProperty system. The Control class is tipically dedicated to elements that have a visual aspect, so probably it would be better we extend a class like DependencyObject for our control to work. Unfortunately we have to use the Control class because all the base classes from FrameworkElement to DependencyObject does not have a public constructor so we are unable to inherit from them. The Control class is not the best choice, but it is the only choice available.

   1: public class CollectionViewSource : Control, IEnumerable, INotifyCollectionChanged
   2: {
   3:     // implement the control... 
   4: }

The class has also to implement two interfaces: IEnumerable and INotifyCollectionChanged. The first interface is required to grant our control the capability to be consumed from an ItemsControl. Using the IEnumerable interface we get two goals. We can assign the CollectionViewSource directly to the ItemsSource property and we can use LINQ to Objects to yield resulting items to the consuming control. Taking advantage of LINQ to Objects will simplify our control structure and get our code to a better level of readability.

   1: #region IEnumerable Members
   2:  
   3: /// <summary>
   4: /// Returns an enumerator that iterates through a collection.
   5: /// </summary>
   6: /// <returns>
   7: /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
   8: /// </returns>
   9: public IEnumerator GetEnumerator()
  10: {
  11:     var result = this.Source
  12:         .Cast<object>();
  13:  
  14:     return result.GetEnumerator();
  15: }
  16:  
  17: #endregion

For the filtering to work we have to expose an event that we'll call every time we have to filter the collection, one time for every item in the collection asking the user to choice the items to show. The simplest thing to do is to create a delegate, that we use as a Where clause lambda expression in the Linq query yielding items. So the delegate will be called by the query every time it needs to examine an element to decide if it has to be yielded or skipped.

   1: /// <summary>
   2: /// Occurs when [filter].
   3: /// </summary>
   4: public event EventHandler<FilterCollectionEventArgs> Filter;
   5:  
   6: /// <summary>
   7: /// Returns an enumerator that iterates through a collection.
   8: /// </summary>
   9: /// <returns>
  10: /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
  11: /// </returns>
  12: public IEnumerator GetEnumerator()
  13: {
  14:     var result = this.Source
  15:         .Cast<object>()
  16:         .Where(FilterItem);
  17:  
  18:     return result.GetEnumerator();
  19: }
  20:  
  21: /// <summary>
  22: /// Filters the item.
  23: /// </summary>
  24: /// <param name="o">The o.</param>
  25: /// <returns></returns>
  26: private bool FilterItem(object o)
  27: {
  28:     FilterCollectionEventArgs args = new FilterCollectionEventArgs(o);
  29:     OnFilter(args);
  30:     return args.Accept;
  31: }
  32:  
  33: /// <summary>
  34: /// Raises the <see cref="E:Filter"/> event.
  35: /// </summary>
  36: /// <param name="e">The <see cref="Elite.Silverlight.FilterCollectionEventArgs"/> instance containing the event data.</param>
  37: protected virtual void OnFilter(FilterCollectionEventArgs e)
  38: {
  39:     EventHandler<FilterCollectionEventArgs> handler = Filter;
  40:  
  41:     if (handler != null)
  42:         handler(this, e);
  43: }

To configure the sorting behavior we have to expose a property that collect the sorting criteria in a way similar to the CollectionViewSource contained in the Windows Presentation Foundation. This require to create a SortDescriptionCollection class that will collect the SortDescription instances. Every SortDescription will require a PropertyName and a Direction property to tell the control wich property use for sorting and the direction of the sorting. All the names I've proposed come directly from the WPF control. Using this namig will grant our CollectionViewSource the maximum compatibility with the WPF control so we may use code snippet created for WPF in Silverlight 2.0.

We use the same LINQ to Objects expression adding OrderBy and ThenBy calls to the query if the sorting is enabled. The sorting will be called immediately after the Where clause.

   1: /// <summary>
   2: /// Returns an enumerator that iterates through a collection.
   3: /// </summary>
   4: /// <returns>
   5: /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
   6: /// </returns>
   7: public IEnumerator GetEnumerator()
   8: {
   9:     var result = this.Source
  10:         .Cast<object>()
  11:         .Where(FilterItem);
  12:  
  13:     foreach (SortDescription sd in this.SortDescriptions)
  14:     {
  15:         if (sd.Direction == ListSortDirection.Ascending)
  16:             result = AppendOrderBy(result, sd.PropertyName);
  17:         else
  18:             result = AppendOrderByDescending(result, sd.PropertyName);
  19:     }
  20:  
  21:     return result.GetEnumerator();
  22: }
  23:  
  24: /// <summary>
  25: /// Appends the order by.
  26: /// </summary>
  27: /// <param name="result">The result.</param>
  28: /// <param name="propertyName">Name of the property.</param>
  29: /// <returns></returns>
  30: private IEnumerable<object> AppendOrderBy(IEnumerable<object> result, string propertyName)
  31: {
  32:     if (result is IOrderedEnumerable<object>)
  33:         return ((IOrderedEnumerable<object>)result).ThenBy(o => GetPropertyValue(o, propertyName));
  34:  
  35:     return result.OrderBy(o => GetPropertyValue(o, propertyName));
  36: }
  37:  
  38: /// <summary>
  39: /// Appends the order by descending.
  40: /// </summary>
  41: /// <param name="result">The result.</param>
  42: /// <param name="propertyName">Name of the property.</param>
  43: /// <returns></returns>
  44: private IEnumerable<object> AppendOrderByDescending(IEnumerable<object> result, string propertyName)
  45: {
  46:     if (result is IOrderedEnumerable<object>)
  47:         return ((IOrderedEnumerable<object>)result).ThenByDescending(o => GetPropertyValue(o, propertyName));
  48:  
  49:     return result.OrderByDescending(o => GetPropertyValue(o, propertyName));
  50: }

With the INotifyCollectionChanged interface we'll notify the control to update his aspect when changes occur. The only thing we have to take care is the way to notify events. The NotifyCollectionChangedEventArgs class we have to use to raise the CollectionChanged event require us to specify the kind of event we are raising. The constructor of this class need to specify a NotifyCollectionChangedAction enumeration parameter that define if we are updating the datasource for Add, Remove, Replace, Move or Reset. To get the ItemsControl updating we need to specify the NotifyCollectionChangedAction.Reset value because every other value does not have an effect on the ItemsControl. This behavior become from the implementation of the ItemsSource property. Inspecting this property with Lutz Roeder Reflector it will show us that it is wrapped with an internal private EnumerableCollectionView that will mask every other type of event. The drawback of this choice is that the target control will be updated entirely instead of handling only the added or removed items. I hope that a future implementation of this control will change this thing, but now we have to accept this behavior to get the CollectionViewSource working.

We have to handle two type of events that raise the CollectionChanged event. The first one is when the underliyng collection raise the same event. This is because if the datasource change we have to reflect this change to the user interface itself forwarding this event. So when the Source property will be assigned we have to hook up its CollectionChanged event (if the source itself implements the INotifyCollectionChanged). For this purpose we create the source property as a DependencyProperty to take advantage of the PropertyChangedCallback that will notify when the Source property change its value. When the value change we have to remove event from a previous instance before to hook the new instance to avoid memory leaks and unexpected results.

   1: public static readonly DependencyProperty SourceProperty =
   2:     DependencyProperty.Register("Source", typeof(IEnumerable), typeof(CollectionViewSource), new PropertyChangedCallback(SourceProperty_Changed));
   3:  
   4: /// <summary>
   5: /// Gets or sets the source.
   6: /// </summary>
   7: /// <value>The source.</value>
   8: public IEnumerable Source
   9: {
  10:     get { return (IEnumerable)this.GetValue(SourceProperty); }
  11:     set { this.SetValue(SourceProperty, value); }
  12: }
  13:  
  14: /// <summary>
  15: /// Sources the property_ changed.
  16: /// </summary>
  17: /// <param name="sender">The sender.</param>
  18: /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
  19: private static void SourceProperty_Changed(DependencyObject sender, DependencyPropertyChangedEventArgs e)
  20: {
  21:     CollectionViewSource cvs = sender as CollectionViewSource;
  22:  
  23:     if (cvs != null)
  24:     {
  25:         if (e.OldValue != null && e.OldValue is INotifyCollectionChanged)
  26:             ((INotifyCollectionChanged)e.OldValue).CollectionChanged -= new NotifyCollectionChangedEventHandler(cvs.CollectionViewSource_CollectionChanged);
  27:  
  28:         if (e.NewValue != null && e.NewValue is INotifyCollectionChanged)
  29:             ((INotifyCollectionChanged)e.NewValue).CollectionChanged += new NotifyCollectionChangedEventHandler(cvs.CollectionViewSource_CollectionChanged);
  30:     }
  31: }
  32:  
  33: /// <summary>
  34: /// Handles the CollectionChanged event of the CollectionViewSource control.
  35: /// </summary>
  36: /// <param name="sender">The source of the event.</param>
  37: /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
  38: private void CollectionViewSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  39: {
  40:     if (sender != null)
  41:         this.OnCollectionChanged(e);
  42: }
  43:  
  44: /// <summary>
  45: /// Raises the <see cref="E:CollectionChanged"/> event.
  46: /// </summary>
  47: /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
  48: protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  49: {
  50:     NotifyCollectionChangedEventHandler handler = CollectionChanged;
  51:  
  52:     if (handler != null)
  53:         handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  54: }

Last but not least we have to expose a method to enable users to force the CollectionViewSource to refresh the criteria and notifying the changes if needed. This is the second type of event we have to handle to notify the collection change. If the user call this method we will assume that something has been changed in the criteria and we raise the CollectionChanged event.

   1: /// <summary>
   2: /// Refreshes this instance.
   3: /// </summary>
   4: public void Refresh()
   5: {
   6:     this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
   7: }

Using the CollectionViewSource control

Now that the control has been completed we have to build a sample project that use the CollectionViewSource. For this purpose I have created an application that read the rss feed of my weblog (http://blog.boschin.it) and display it in an ItemsControl. The interface have a TextBox that we use to filter the post by title and two radio buttons that decide if sort the items ascending or descending by the publish date. The application is very simple but its purpose is only to dimostrate how to use the CollectionViewSource control and how simple is to use and customize its behavior. Here is a brief markup defining the application interface:

   1: <UserControl x:Class="Silverlight.Page"
   2:     xmlns="http://schemas.microsoft.com/client/2007" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:     xmlns:s="clr-namespace:Silverlight" Loaded="UserControl_Loaded"
   5:     xmlns:e="clr-namespace:Elite.Silverlight;assembly=Elite.Silverlight"
   6:     xmlns:ed="clr-namespace:Elite.Silverlight.Data;assembly=Elite.Silverlight"
   7:     Width="400" Height="400">
   8:     <UserControl.Resources>
   9:         <s:DataSource x:Name="dataSource" />
  10:         <ed:CollectionViewSource x:Name="rssView" Source="{Binding Items, Source={StaticResource dataSource}}" Filter="rssView_Filter">
  11:             <CollectionViewSource.SortDescriptions>
  12:                 <ed:SortDescription PropertyName="PublishDate" Direction="Ascending" />
  13:             </CollectionViewSource.SortDescriptions>
  14:         </ed:CollectionViewSource>
  15:         <e:StringToUriConverter x:Name="stringToUri" />
  16:         <e:FormatDateTimeConverter x:Name="dateToString" />
  17:     </UserControl.Resources>
  18:     <Grid x:Name="LayoutRoot" Background="#ffffffff">
  19:         <Grid.RowDefinitions>
  20:             <RowDefinition Height="*" />
  21:             <RowDefinition Height="80" />
  22:         </Grid.RowDefinitions>
  23:         <Grid.Resources>
  24:             <DataTemplate x:Name="rssTemplate">
  25:                 <Border Background="#ffeeeeee" Margin="2,2,2,0" Padding="5" BorderBrush="#00000000" BorderThickness="1" CornerRadius="3,3">
  26:                     <Grid>
  27:                         <Grid.ColumnDefinitions>
  28:                             <ColumnDefinition Width="50*"/>
  29:                             <ColumnDefinition Width="50*"/>
  30:                         </Grid.ColumnDefinitions>
  31:                         <Grid.RowDefinitions>
  32:                             <RowDefinition Height="60*" />
  33:                             <RowDefinition Height="40*" />
  34:                         </Grid.RowDefinitions>
  35:                         <HyperlinkButton x:Name="itemLabel" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="3,3,3,3" 
  36:                                          Content="{Binding Title.Text}" NavigateUri="{Binding Id, Converter={StaticResource stringToUri}}" 
  37:                                          TargetName="_blank" FontSize="14" FontFamily="Verdana" Foreground="#ffff9900" />
  38:                         <TextBlock Grid.Column="0" Grid.Row="1" Margin="3,3,3,3" FontSize="14" FontFamily="Verdana" 
  39:                                    Foreground="#ff666666" Text="{Binding PublishDate, Converter={StaticResource dateToString}, ConverterParameter=dd.MM.yyyy - hh:mm}" />
  40:                         <TextBlock Grid.Column="1" Grid.Row="1" Margin="3,3,3,3" FontSize="14" FontFamily="Verdana"
  41:                                    Foreground="#ff666666" Text="{Binding Copyright}" />
  42:                     </Grid>
  43:                 </Border>
  44:             </DataTemplate>
  45:         </Grid.Resources>
  46:         <Border Grid.Column="0" Grid.Row="0" Margin="2" BorderBrush="#ffff9900" BorderThickness="1" CornerRadius="3,3">
  47:             <ScrollViewer DataContext="{StaticResource dataSource}" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
  48:                 <ItemsControl ItemsSource="{Binding Source={StaticResource rssView}}" ItemTemplate="{StaticResource rssTemplate}">
  49:                 </ItemsControl>
  50:             </ScrollViewer>
  51:         </Border>
  52:         <Grid Grid.Column="0" Grid.Row="1" Background="#55ff9900" Margin="2">
  53:             <Grid.ColumnDefinitions>
  54:                 <ColumnDefinition Width="*"/>
  55:                 <ColumnDefinition Width="*"/>
  56:             </Grid.ColumnDefinitions>
  57:             <Grid.RowDefinitions>
  58:                 <RowDefinition Height="50*" />
  59:                 <RowDefinition Height="50*" />
  60:             </Grid.RowDefinitions>
  61:             <RadioButton x:Name="sortDescending" TextAlignment="Right" HorizontalAlignment="Left" Margin="10" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="1" Content="Descending" GroupName="sort" Checked="sort_Checked" IsChecked="true" />
  62:             <RadioButton x:Name="sortAscending" TextAlignment="Left" HorizontalAlignment="Right" Margin="10" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="1" Content="Ascending" GroupName="sort" Checked="sort_Checked" />
  63:             <WatermarkedTextBox x:Name="txtKey" Padding="3" Grid.Column="0" Grid.Row="0" Margin="5" FontSize="14" Grid.ColumnSpan="2" TextChanged="txtKey_TextChanged" Watermark="Insert Keywork..." />
  64:         </Grid>
  65:     </Grid>
  66: </UserControl>

In this code snippet it is visible how to bind correctly the datasource to the CollectionViewSource and the CollectionViewSource itselft to the consuming ItemsControl. When we have established this connection we hook up the filtering event where we use a Contains() to check that the element contains the text written in the textbox. Every time we press a key the TextChanged event simply call the Refresh() method of the CollectionViewSource causing the filtering to happen again with the new text entered. This is a very intensive use of the  CollectionViewSource but with a few collection items (up to one or two hundred) there will be no side effects. In a production application this scenario probably will be handled in a different way, but I'm sure there is many other times you may take advantage of my control. The sorting happen in a similar way. When a radio button has been hit we swap the direction of the configured sorting and then recall the Refresh() method to rebind the interface. Try changing the SortDescription properties to sort by another property.

   1: public partial class Page : UserControl
   2: {
   3:     public Page()
   4:     {
   5:         InitializeComponent();
   6:     }
   7:  
   8:     private void UserControl_Loaded(object sender, RoutedEventArgs e)
   9:     {
  10:         this.dataSource = this.Resources["dataSource"] as DataSource;
  11:         this.dataSource.Load();
  12:     }
  13:  
  14:     private void sort_Checked(object sender, RoutedEventArgs e)
  15:     {
  16:         CollectionViewSource rssView = this.Resources["rssView"] as CollectionViewSource;
  17:  
  18:         if (rssView != null)
  19:         {
  20:             SortDescription sort = rssView.SortDescriptions[0];
  21:             sort.Direction = (sender == sortAscending ? ListSortDirection.Ascending : ListSortDirection.Descending);
  22:             rssView.Refresh();
  23:         }
  24:     }
  25:  
  26:     private void txtKey_TextChanged(object sender, TextChangedEventArgs e)
  27:     {
  28:         rssView.Refresh();
  29:     }
  30:  
  31:     private void rssView_Filter(object sender, Elite.Silverlight.FilterCollectionEventArgs e)
  32:     {
  33:         SyndicationItem item = e.Item as SyndicationItem;
  34:  
  35:         if (item != null)
  36:         {
  37:             e.Accept = item.Title.Text.ToLower().Contains(txtKey.Text.ToLower()) || txtKey.Text == txtKey.Watermark || txtKey.Text == string.Empty;
  38:         }
  39:     }
  40: }

Now we may run the application and try to enter some text in the TextBox. We will see that the displayed item will change according to the text entered. Also checking and unchecking the sort direction radio button will change the order of the the items.

Conclusion

The CollectionViewSource control is part of my Elite.Silverlight Library 1.0.3037.0 where I'm collecting all my reusable controls and classes. Please download it from codeplex (http://www.codeplex.com/silverlightlibrary) and use for free in your projects. The project come under the CCPL license and is free for non-commercial purposes. For use in commercial project please contact me on my weblog contact form at (http://blog.boschin.it/contact.aspx).