Thursday, April 22, 2010

Using an Observable while still supporting multiple ViewModel instances

This post follows on from my previous post, Using the Observable for Change Notification in Calculated/Dependent Properties.

An upcoming project has the need for the Views in a module to be loaded multiple times in potentially many different places in the Shell. This made me realise the shortcoming of my post that describes using an Observable to share data between Views. The problem is in how the module registers the Observable. The code is below:

protected void RegisterViewsAndServices()
{
...
_container.RegisterInstance(
_container.Resolve<ISelectedContactObservable>());
...
}


This works well in the example project give in my previous post, but it all goes awry when we want to load multiple instances of our Views, because each instance of the View will needs its own Observable. By registering an instance with Unity in this way, our hands are tied.


So what to do instead? Well, following on from another previous post, we can use a controller to overcome this limitation. The controller becomes responsible for calling Unity and obtaining our Views. At this time it can therefore also create a fresh instance of the Observable and supply it to the ViewModels. The code from the attached project (at the end of this post) looks like this:


public class MainModuleController :
IMainModuleController
{
...
/// <summary>
/// Listens for requests to load the View
/// into a Region.
/// </summary>
///
public void LoadViewsEventHandler(
LoadViewsEventArgs payload)
{
// Create a fresh Observable that is used to share data
// between the two Views
ISelectedContactObservable observable =
_container.Resolve<ISelectedContactObservable>();
observable.SelectedContact = null;

// Create the Views using Unity
...
// Assign the Observable to each of them
detailsViewModel.SelectedContact = observable;
selectionViewModel.SelectedContact = observable;
...
}
}


Using the controller this way ensures correct creation of Observable classes.


The sample project can be downloaded here
.

Wednesday, April 21, 2010

Ensuring your Prism Modules can load multiple instances in your UI

In many Prism examples I have seen, such as the StockTrader Reference Implementation, it's not unusual to see a module register its View with the region it wants it loaded into. For example, the StockTrader's MarketModule has code like this:
public void Initialize()
{
...
this.regionManager.RegisterViewWithRegion
(RegionNames.ResearchRegion, () =>
this.container.Resolve
<ITrendLinePresentationModel>().View);
}

This has never really sat all that well with me, and it wasn't until recently that I realised why. An upcoming project has the requirement to not only load multiple instances of a Module's Views, but to load them in different Regions that could be user defined. Code like that above simply won't work in a situation like this. Unfortunately, it's also the way I've been writing my applications, and now I wish I hadn't.


So, what's a better way to have Views loaded into regions? I think the first thing to do is to never make a Module responsible for registering its Views with Regions. Instead, have a Module perform it's initialisation (such as registering all its types with Unity) and then sit there and wait for instructions on what to do next. A good idea could be to have it listen for a composite event that tells it when to load a View, and the name of the Region into which it should be loaded. By taking responsibility for determining when and where a View should be loaded away from the Module, it becomes so much more re-usable.


So what component should tell the Module what to do next? A reasonable choice would be the Shell itself, after all, the Shell is the component that defines the Regions, so it is therefore a good candidate for knowing what they should be used for.


Inside the Module itself, it will listen for load requests and respond accordingly. The code snippet below shows a method inside the Module Controller that when called will load the Modules Views into the Regions specified inside the Composite Event's payload:

/// <summary>
/// Listens for requests to load the View into a Region.
/// </summary>
/// <param name="payload">The regions to load into</param>
public void LoadViewsEventHandler(LoadViewsEventArgs payload)
{
// Create the Views using Unity
Views.IDetailsViewModel detailsViewModel =
_container.Resolve<Views.IDetailsViewModel>();

// Inject them into the given Regions
_regionManager.AddToRegion(payload.DetailsRegion,
detailsViewModel.View);
}


And lastly, the code snippet below shows the code inside the Shell's ViewModel that is called after all the Modules have been initialised. It loads three new instances of the Module's Views into different Regions inside the Shell:

public void LoadViews()
{
// Load all the views we want to show in the Shell
_eventAggregator.GetEvent<LoadViewsEvent>().Publish(
new LoadViewsEventArgs()
{
DetailsRegion = "DetailsRegion1",
});

_eventAggregator.GetEvent<LoadViewsEvent>().Publish(
new LoadViewsEventArgs()
{
DetailsRegion = "DetailsRegion2",
});

_eventAggregator.GetEvent<LoadViewsEvent>().Publish(
new LoadViewsEventArgs()
{
DetailsRegion = "DetailsRegion3",
});
}

I've included a sample project that demonstrates the concepts described here.

Friday, April 9, 2010

Just spent ages trying to get csExWB to work on Windows 7 64 bit

Am playing with csExWB to try to get IE to do things it normally wouldn't. Couldn't get it working on my 64 bit Windows 7 installation. After spending a ridiculous amount of time Googling, found the answer here.