Over a million developers have joined DZone.

Silverlight, M-V-VM ... and IoC - part 3

· Mobile Zone

It’s time to go on with this series, at this point we know the basic principles of MVVM and we have a set of base classes and interfaces we can use. We need to put it in action and see how we can implement the View and the ViewModel and how we can tie them together with an IoC container.

Note: all the samples I post here directly derive for the ‘test project’ I use for all my Silverlight based workshops and experiments. I have published it on Codeplex and I’m moving to it from my private Subversion...I’ll try to reconstruct the history of the check-ins following the same major step stones I had in my private repository.

You can find the project at this link: http://dnmmusicstoresl.codeplex.com/. The code presented here refers to the Changeset 41379, this was a direct porting from a Silverlight 3 project (no RIA services, no custom behavior nor commanding engine was used at this stage).

The scenario: we are building a back-office for a Music Store Shop, they have a list of albums in their store and we want to build a form that allow us to perform simple queries on the data, we give for granted the ‘server-side’ of the whole thing (we have a WCF web service that we can reference and that will expose a SearchAlbum(string) function that will filter the data).

The goal: build a ViewModel that expose the data and some functions the View can bind to and use, buildup a View that is able to use the ViewModel, then configure our IoC container to be able to resolve the object tree we need to show the form to the user.

The model: at this stage I kept the things simple (no new technologies like RIA services yet): ‘server side’ I have a simple AlbumSummary class that will contain all the information we need to display, ‘client side’ we make a reference to the web service and we use the proxy classes that it will generated for us.

/// <summary>
/// class returned by the search it contains some informations about the albums
/// </summary>
[DataContract]
public class AlbumSummary : DomainObject<int>
{
[DataMember]
public virtual string Title { get; set; }

[DataMember]
public virtual string Author { get; set; }

[DataMember]
public virtual string Label { get; set; }

[DataMember]
public virtual string Genre { get; set; }

[DataMember]
public virtual DateTime PublicationDate { get; set; }

/// <summary>
/// will contain the url (if we are in a web environment) or the path (if desktop appplication) of the image
/// this will be usually computed using the album id
/// </summary>
[DataMember]
public virtual string Image { get; set; }
}

We are now about to face our first architectural issue, when building the View and the ViewModel we need to keep in mind we are going to use them with an IoC container, so we need to care about the dependencies and the creation order of the objects. We have to decide which is the ‘master’ object we are going to resolve, we have two approaches here:

  • Resolve a ViewModel: you ask for a ViewModel first, it will need a mandatory dependency (constructor injection) to a View otherwise you cannot show anything to the user or you need to rely on external methods to glue the ViewModel to the View you choose to display; the View will have an optional dependency to the ViewModel.
  • Resolve a View: you ask for a View fist, it will need a mandatory dependency to a ViewModel otherwise you have just an empty series of controls you cannot interact with (the behavior is in the ViewModel). The ViewModel will have an optional dependency to the View.


Obviously you cannot have constructor injection for both the View and the ViewModel because of a circular reference issue; so you have to pick one of this two approaches and maybe rely of some extensions of the IoC container to automatically inject the optional references (we’ll see a Castle Windsor facility implementation later).

In the demo application I chose to follow the second approach because the ViewModel should be able to operate correctly even without a View attached at all (to simply testing even more), plus I just thought it could work better with some experiments I made with the Silverlight Navigation Framework (see my previous post: Silverlight Navigation Framework: resolve the pages using an IoC container) and it should be a bit easier to configure multiple Views that work with the same ViewModel.

The ViewModel: I started defining the interface, it must have a collection to hold data and a method that will be used to perform the search (the web service accepts a string and searched through various fields of the Album entity):

public interface ISearchViewModel : IViewModel
{
new ISearchView View { get; }

/// <summary>
/// Collection that will be used to expose the result of the search operation
/// </summary>
PagedCollectionView SearchResults { get; set; }

/// <summary>
/// performs an asyncronous search throught the webservice and returns the results filling in the SearchResults collection
/// </summary>
/// <param name="query"></param>
void PerformSearch(string query);
}

And its implementation:

/// <summary>
/// glues the interface with the data model, exposes the data that will be used by the view
/// </summary>
public class SearchViewModel : ViewModel, ISearchViewModel
{
public SearchViewModel()
{
SearchResults = new PagedCollectionView(_internalSearchResult);
}

public new ISearchView View { get { return (this as IViewModel).View as ISearchView; } }

public ILogger Logger { get; set; }

/// <summary>
/// collection that holds the actual data
/// </summary>
private readonly ObservableCollection<AlbumSummary> _internalSearchResult = new ObservableCollection<AlbumSummary>();

/// <summary>
/// object that will be used by the binding to expose the collection of data resulting from the search
/// </summary>
public PagedCollectionView SearchResults { get; set; }

/// <summary>
/// performs an asyncronous search throught the webservice and returns the results filling in the SearchResults collection
/// </summary>
/// <param name="query"></param>
public void PerformSearch(string query)
{
IsBusy = true;
MusicStoreServiceClient service = ServiceHelper.GetServiceClient(); ;
service.SearchAlbumsCompleted += (sender, e) =>
{
if (e.Error == null)
{
_internalSearchResult.Clear();
foreach (var result in e.Result)
_internalSearchResult.Add(result);
Logger.Info("VM - Search Completed");
IsBusy = false;
}
else
{
Logger.ErrorFormat(e.Error, "VM - Error performing search with query: {0}", query);
IsBusy = false;
throw e.Error;
}
};
Logger.InfoFormat("VM - Performing search with query: {0}", query);
service.SearchAlbumsAsync(query);
}

}

The View: I’ll skip the XAML that simply defines a textbox for the search, two buttons to perform the search operation and to add a new album to the back-office, a grid to display the results together with its pager (you can get this code from the repository), and I’ll show you the code behind file. 

public interface ISearchView : IView
{
new ISearchViewModel ViewModel { get; }
}
public partial class Search : Page, ISearchView
{
protected Search()
{
InitializeComponent();

Loaded += Search_Loaded;
}

public Search(ISearchViewModel viewModel)
: this()
{
(this as IView).ViewModel = viewModel;
}


To be able to bind the controls to the properties of the ViewModel we need to set it as the DataContext of the control (this is done in the constructor because the ViewModel property is implemented as a wrapper over the DataContext); you can now bind directly to the properties exposed by the ViewModel.

I am not using a declarative approach here (that is declaring the ViewModel as a resource in the View’s XAML and bind to that) because I want to be able to inject it into the view using an IoC container.

This is basically how you were forced to do MVVM in Silverlight 3 without using any custom behavior or commanding framework, you just wired to the events raised by the controls and forwarded the calls to the ViewModel’s functions.

Right now we have all the pieces of the mosaic and we can configure the IoC container (wrapped by a Service Locator in my example, because I like to test multiple IoC frameworks). Actually the sample uses a porting of Castle Windsor to Silverlight 4 so we’ll use its syntax to configure the bits (feel free to replace it with your favorite IoC system).

At the application startup we can call a bootstrap function that just configure the container:

private void Bootstrap()
{
WindsorContainer Container = new WindsorContainer();

Container.Kernel.AddFacility("ViewModelInitializationFacility", new ViewModelInitializationFacility());

Container.Kernel.AddFacility("LoggingFacility",
new LoggingFacility(new LoggerConfig() { Level = LoggerLevel.Debug, AppendersNames = new[] { "DefAppender" } }));
Container.Register(
Component.For<IAppender>().ImplementedBy<BrowserConsoleAppender>().Named("DefAppender"),
Component.For<IInterceptor>().ImplementedBy<LoggerInterceptor>().Named("LoggerInterceptor")
);

// Features registration
Container.Register(
// application infrastructure and shell configuration
Component.For<IPageProvider>().ImplementedBy<PageProvider>(),
Component.For<INavigationContentLoader>().ImplementedBy<IocNavigationContentLoader>(),
Component.For<MainPage>(),

// search Feature
Component.For<ISearchViewModel>().ImplementedBy<SearchViewModel>().LifeStyle.Custom<Structura.Castle.Windsor.Lifecycle.SingletonLifestyleManager>(),
Component.For<ISearchView>().ImplementedBy<Search>().Named("Search"),

// album management feature
Component.For<IAlbumViewModel>().ImplementedBy<AlbumViewModel>().Interceptors(InterceptorReference.ForKey("LoggerInterceptor")).Last,
Component.For<IAlbumView>().ImplementedBy<Album>().Named("Album")
);

ServiceLocator = new CastleWindsorServiceLocator(Container);
}


We first create the container and initialize it with two facilities one for Logging (refer to my two previous posts about Castle WIndsor and logging in Silverlight) and a facility we use to ‘inject’ the optional View dependency into the ViewModel right after the View creation:

public class ViewModelInitializationFacility : AbstractFacility
{
protected override void Init()
{
this.Kernel.ComponentCreated += new ComponentInstanceDelegate(Kernel_ComponentCreated);
}

void Kernel_ComponentCreated(ComponentModel model, object instance)
{
// we configure the ViewModel injecting the optinal dependency to the view
IView view = instance as IView;
if (view != null)
{
view.ViewModel.View = view;
}
}
}


The custom Singleton Lifecycle you see up here is a workaround for a Castle.Windsor bug (see http://support.castleproject.org/projects/IOC/issues/view/IOC-ISSUE-192) and it will be removed once fixed.

Now to have a fully configured and usable View object we can just ask the Container to resolve the object by name or by its type.

What we obtained: the view and the ViewModel are fully decoupled and they talk only using interfaces, which means we can swap any of them for testing purpose or just because we have a better implementation or maybe due to branding or skinning requirements; everything is hold inside the container which becomes a central piece in your application architecture (because it’s also responsible for the objects lifecycle).

We are however far from a ‘pure’ implementation of the pattern, we have a lot of code-behind in the View, while the pattern requires (if possible) a fully declarative approach; the next step will show you how you can move to a ‘better’ implementation using the new Silverlight 4 default commanding infrastructure (we’ll also see some nice things you can do using AOP).

The full source code is available on CodePlex.

Topics:

Published at DZone with permission of Alessandro Giorgetti, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}