Saturday, November 7, 2009

Silverlight Rx : System.Reactive

Yesterday I came across the System.Reactive assembly that is included with the Silverlight Toolkit.  On the blog of Jafar Husain

He has made a lot of posts about the Rx framework, and it is definitely interesting to read!

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html

We can program in reaction to events and apply our logic on them. We could for example do some action only when an event is raised with a certain event argument. This embeds the Async handling of events and allows this code to be unit tested.

The big problem with asynchronous programming is that the code very quickly gets unreadable and hard to maintain, as it is not always clear how several events are linked to one another.

The Reactive framework works with the IObserver and the IObservable classes, which can be thought of as Reactive versions of IEnumerable in the context of Linq ( actually the extension methods have been added to the System.Linq namespace ).  You basically look at the events being thrown as a collection you can browse trough.

So how does it work.

To quickly show how we can use Rx to simplify our code, We will write an extension method that will call a web client's DownloadString method and return a String.

public static class WebClientExtender
{

public static IObservable<string> GetSite(this WebClient client, string url)
{
var downloaded = Observable.FromEvent<DownloadStringCompletedEventArgs>
                 (client, "DownloadStringCompleted");
client.DownloadStringAsync(new Uri(url));
return downloaded.Select(x=>((DownloadStringCompletedEventArgs)x.EventArgs).Result);
}

}

To call this we can now simple add the following line of code to our Silverlight application.

new WebClient().GetSite("http://localhost/").Subscribe(x => MessageBox.Show(x));

This way we can very easily clean up the code and structure how the Asynchronous events are handled.

Sunday, November 1, 2009

CollectionViewSource in Silverlight

What is CollectionViewSource

The CollectionViewSource is a class that is used in WPF to apply sorting and filtering on Data. We can also use this class in Silverlight 3.
I came across it while studying for my WPF exam and noticed it was also available in Silverlight. So naturally I had a look. It is very easy to use as I will demonstrate below.

So lets have a look.

In this case, we have List of Person we want be able to Filter and Sort correctly.

A person could be defined like this :

public class Person
{
public String FirstName { get; set; }
public String LastName { get; set; }
}

To be able to use a CollectionViewSource we can define it as a resource in our XAML, we could also define it in code, but for this example we will write most in XAML.

<UserControl.Resources>
<local:PersonDataSource key="data">
<CollectionViewSource Source="{Binding Source={StaticResource data}}" x:Key="CollVS">
<CollectionViewSource.SortDescriptions>
<ComponentModel:SortDescription Direction="Descending">
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</UserControl.Resources>

You see here that we defined a "data" datasource, we should create a class that will be able to store the information we need, lets call it PersonDataSource. We could also add some logic here to return some dummy data for now. For this example I chose to simply inherit from ObservableCollection and not add any additional logic, obviously you could use any collection here you would normally use to bind to a datagrid.

To finish our XAML UI, we add a DataGrid to display our data and a TextBox we can use for filtering. In our case we will apply filtering on the LastName.

<StackPanel Orientation="Vertical">
<data:DataGrid ItemsSource="{Binding Source={StaticResource CollVS}}"/>
<TextBox x:Name="filter"/>
</StackPanel>

Now we can define how we are going to do our filtering and make sure the filtering is visible in the User interface.

public MainPage()
{
InitializeComponent();
//Get the CollectionViewSource from the Resource of the UserControl
CollectionViewSource CollVS = (CollectionViewSource)Resources["CollVS"];
//Make sure we update the datagrid when we change our filter
filter.TextChanged += delegate { CollVS.View.Refresh(); };
//Subscribe to the filter event
CollVS.Filter += cvs_Filter;
}

void cvs_Filter(object sender, FilterEventArgs e)
{
e.Accepted = ((Person)e.Item).LastName.ToLower().StartsWith(filter.Text.ToLower());
}

So what did we do here ? First we needed to get our CollectionViewSource from our Resources. The most important thing here is that we do the following :

filter.TextChanged += delegate { CollVS.View.Refresh(); };

If we don't do this, our view will not update and we will not be able to see our filter.

To filter, we can change if an Item should be filtered by setting the Accepted property , the Filter will be called for every element in your collection.

The last step is to implement IComparable on our Person object , this is required by the CollectionViewSource to apply sorting on the Collection. The data will now be automatically sorted as it is filtered.