Monday, January 18, 2010

Using an Observable Object to share data between ViewModels

A good option for sharing data between ViewModels in Prism is to implement a simple Observer pattern. The Observable Object has a registered instance inside Unity and can be exposed to any ViewModel that needs to read/write to the shared data it holds.

The Observable implementation I describe below can really be thought of as a purpose-built, shared ViewModel. So why not call it a ViewModel? Simply because of one important difference - the Observable Object has no View. Furthermore, the name Observable implies that the class will be shared among any number of interested parties, rather than being tied to a View.

Step 1. Defining the Observable class

The class must implement INotifyPropertyChanged. E.g.:

public class SelectedContactObservable : INotifyPropertyChanged,
ISelectedContactObservable
{
...
}


Step 2. Configuring Unity

Unity is configured to use the class, then a single instance is registered so that it may be shared among all consumers.

_container.RegisterType(typeof(ISelectedContactObservable),
typeof(SelectedContactObservable));
_container.RegisterInstance(_container.Resolve<ISelectedContactObservable>());

Step 3. Use the Observable Object in your ViewModels

Use the Observable Object in any number of consumer classes (ViewModels) using constuctor injection.

public class ContactListViewModel : INotifyPropertyChanged
{
public ContactListViewModel(ISelectedContactObservable
selectedContactObservable)
{
...
}

public ISelectedContactObservable SelectedContactObservable
{
...
}
}

Step 4. Databind to the Observable Object in your Views

<TextBlock Text="{Binding SelectedContactObservable.SelectedContact.LastName}" />
<TextBlock Text="{Binding SelectedContactObservable.SelectedContact.FirstName}" />

Note that the actual observable object itself is exposed as a property on the ViewModel (rather than having some or all of its properties wrapped inside the ViewModel). This allows the change notifications to work with the databinding without the need of any additional code.

UPDATE: I have further worked this scenario to support dependent/calculated properties in this post.

Sunday, January 17, 2010

Justin Angel's Automatic INotifyPropertyChanged

Justin Angel has come up with another way to auto implement INPC. This time using low-level IL re-writing in the DLLs in after the project has been compiled. Advantages to this from my perspective are:
  • Minimal intrusion into the application's code;
  • Will work with Prism easily as it does not need to use proxy creators that must integrate with Prism/Unity;
  • No extra dependencies on the project... Sort of... Since there has to be adding of some DLLs that are exectued post-build to tweak the IL;

What I don't like (at this stage at least) is:

  • Bleeding edge example that is probably not ready for production use yet (at one point in the code the comments actually say "This is the worst hack in the history of the known universe"!;
  • I had numerous issues compiling the project, with it locking files, which is no doubt related to the hack mentioned above, as it has to create temporary copies of DLLs in order to tweak the IL;
  • I suspect it will only work for automatic properties, so if the property is made more complex with validation code etc. (as most of ours are, as we perform data validation in our ViewModel property setters - to support Silverlight 3 data validation). This may in fact not be the case, but I spent too much time working with the compilation issues and never got the solution building reliably enough to be able to start modifying the code to find out;

It's still very interesting, and hopefully something more solid will come of it, but at this stage it's not ready for me to start using in our systems.

Thursday, January 14, 2010

Another Automatic INotifyPropertyChanged Contender

ActiveSharp is another option here. However I don't like it since I suspect what it does is simple enough to do myself, and you still have to implement various piece of the infrastructure yourself.

I guess I'm just hanging out for Silverlight Unity to implement Interception. Seeing as the problem I am looking to solve is not a big one, I'd rather live with it than choose a solution that is sub-optimal.

Wednesday, January 13, 2010

In Summary: Automating INotifyPropertyChanged in Silverlight/Prism

Well, I've about exhausted my options and it's safe to say there is currently no quick win here.
  • Option 1: PostSharp : Has issues with derived types that break databinding
  • Option 2: Unity with Interception : Interception has not been implemented in Unity for Silverlight. Seems there are no plans on the horizon to do it either.
  • Option 3: Using Castle DynamicProxy : Might have been easy to do if I had been able to use the Prism Contrib project's Windsor adapter, but unfortunately it has not been updated for Prism V2.
  • Option 4: Use DynamicProxy with Unity : Would probably require writing a custom Unity extension. Not something I have time for right now.

So I think I'll have to leave it at that for the moment and move on.

No Interception for Silverlight Unity 1.2 :-(

And from what I can see there are no plans to add it. Thankfully there is Castle Dynamic Proxy, but so far with everything I've done in Prism, we've used Unity. Since I want to be able to do some AOP for automating INotifyPropertyChanged, and (hopefully) simplify using the Event Aggregator for shared data, I'm going to look into using Castle Windsor with Prism.

Luckily a nice person has already added support to the Prism contrib project. It looks a bit old, so hopefully it supports Silverlight.

UPDATE: It is too old. The Windsor adapter is for Prism V1, with no update for V2. Hmmmmm... I'm running out of options. Perhaps if I can get Castle Dynamic Proxy to work with Unity??

Using PostSharp 1.5 to automate INotifyPropertyChanged

I've spent some time today working with PostSharp for Silverlight, trying to use it to simplify implementing the INotifyPropertyChanged interface on my view models. Ultimately I hope to also simplify doing change notification via Prism's Event Aggregator too.

Unfortunately, as nice a solution as this appears to be, I will probably be throwing it away. The view model below works fine when databound:

[NotifyPropertyChanged]
public class MyViewModel
{
...
public int Age
{
get;
set;
}
...
}

However, this one breaks databinding - no change notification works at all:

[NotifyPropertyChanged]
public class MyBaseViewModel
{
...
}

public class MyViewModel
{
...
public int Age
{
get;
set;
}
...
}

So next I think I'll be looking at using Castle Dynamic Proxy to do it for me. Jonas Follesoe has been discussing it recently. What's extra nice is that he's also dealt with dependent properties too.

Tuesday, January 12, 2010

Sharing Objects Between ViewModels

An application we recently finished off at work highlighted a weakness within our architecture when it came to sharing data in the UI between the various ViewModels within our module.

Imagine an application with two screens (Views), arranged in a wizard work-flow way. The first screen allows you to select a file, and the second displays the properties of that file. The screen has typical “Next” and “Back” buttons and each View has its own ViewModel (say SelectionViewModel and PropertiesViewModel).

Upon selecting a file in the SelectionViewModel, how should the PropertiesViewModel receive/store the data that it needs to display?

In the case of our application, two different approaches were implemented in various places, both of which were quite unappealing. Here they are:

Approach 1: Binding to a shared ViewModel

One solution was to give PropertiesViewModel a reference to the SelectionViewModel and databind to it in the View. The constructor for the PropertiesViewModel would receive the SelectionViewModel instance, fed to it by Unity. Unity was also configured to store a single instance of the SelectionViewModel by using the RegisterInstance() method.

A sample piece of XAML in the PropertiesView might look like this:
<TextBlock Text="{Binding PropertiesViewModel.SelectedFile.Filename}" />

Approach 2: Using Events with a shared ViewModel

Imagine the same application, but this time rather than binding to the shared ViewModel, properties are created for binding, and are updated when the shared ViewModel changes. For example, imagine a Filename property created in the PropertiesViewModel, defined like any other. However, since the Filename is dependent upon which file is selected, eventing is used to listen for changes on the SelectionViewModel and update the Filename accordingly.

The PropertiesViewModel still needs to hold a reference to the SelectionViewModel, but this time it takes advantage of the fact that it implements INotifyPropertyChanged to listen for changes on it. E.g.:
/// <summary>
/// Initialise data for this view model
/// </summary>
public void Initialise()
{
// Because we are exposing properties reliant on SelectionViewModel, we
// are interested in knowing when properties change on that object.
SelectionViewModel.PropertyChanged += SelectionViewModelPropertyChanged;

...

}

...

/// <summary>
/// Fires when a property on the attachments SelectionViewModel changes
/// </summary>
/// <param name="sender">not used</param>
/// <param name="e">the property that has changed</param>
private void SelectionViewModelPropertyChanged (object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedFile")
{
OnPropertyChanged("Filename");
}
}

...

/// <summary>
/// Return the text to be displayed as the filename.
/// </summary>
public String Filename
{
get
{
return _ SelectionViewModel.SelectedFile.Filename;
}
}


So what’s wrong with these approaches? Right from the start it felt dirty to be passing around instances of one ViewModel to another (thus creating dependencies between them), and using the RegisterInstance method of Unity to ensure we only ever had one instance. It seems there should be some much cleaner approaches, and I think that using Prism’s Event Aggregator it should be easy enough to do. I’m planning on looking into this over the next few days if I find the time and hopefully post some better options soon.