Coproject - a RIA Caliburn.Micro demo, part 3

by Augustin Šulc

In this third part, we will set up Caliburn.Micro to our client project and add some cool styles :-).

1. Project cleanup

Let’s create some structure in the project. Delete MainPage.xaml and create folders Assets, ViewModels, ViewModels/Interfaces, and Views. Your Solution Explorer should look like this:
image

Finally, to make the solution build, make function Application_Startup in App.xaml.cs empty (we have deleted MainPage).

2. Add Caliburn.Micro

Open Coproject client in Solution Explorer, and Add Reference to Caliburn.Micro.dll. You will probably have to browse for the file in /Bin/Release/ folder where you downloaded and built it in Step 0. Make sure that you reference the Silverlight version of C.M.

Add Bootstrapper

In root, create new class AppBootstrapper and make it inherit Caliburn.Micro.Bootstrapper. This works as C.M initialization and configuration. Now, since we want to use MEF as Dependency Injection container, we need to set the bootstrapper as follows:

public class AppBootstrapper : Bootstrapper
{
private CompositionContainer _container;

protected override void Configure()
{
InitializeContainer();
}


private void InitializeContainer()
{
AggregateCatalog catalog = new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());

_container = CompositionHost.Initialize(catalog);

var batch = new CompositionBatch();

IEventAggregator eventAggregator = new EventAggregator();
IWindowManager windowManager = new WindowManager();
eventAggregator.Subscribe(windowManager);

batch.AddExportedValue<IEventAggregator>(eventAggregator);
batch.AddExportedValue<IWindowManager>(windowManager);
batch.AddExportedValue(_container);

_container.Compose(batch);
}

protected override object GetInstance(Type serviceType, string key)
{
string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
var exports = _container.GetExportedValues<object>(contract);

if (exports.Any())
{
return exports.First();
}

throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}

protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
}

protected override void BuildUp(object instance)
{
_container.SatisfyImportsOnce(instance);
}
}

You’ll have to add references to:

System.ComponentModel.Composition
System.ComponentModel.Composition.Initialization

And add usings to (probably more usings will be needed, but these ones are not obvious):

using System.Linq;
using System.ComponentModel.Composition;

This will initialize MEF and let it auto discover exports throughout the whole assembly.

Note: Caliburn.Micro is not bound to any concrete implementation of DI, so you can set it to use Unity, Ninject, Castle Windsor container, or any other.

Add Shell

In ViewModels/Interfaces, create a new empty interface called IShell. We will use it to mark the main window (shell). Then, create Shell implementation:

[Export(typeof(IShell))]
public class ShellViewModel : Screen, IShell
{
}

Notice the Export attribute. It tells MEF to consider Shell an IShell implementation. So, if anyone ‘asks’ MEF for IShell implementation, MEF will provide a ShellViewModel instance.

Now, when we have simple shell viewmodel, we also need a view. Create a new Silverlight User Control in Views so that the project structure looks like:
image

Add a TextBlock with text Coproject to the view.

Link it all together

Now, when we have shell prepared, there are only two more things to do: notify C.M about our shell and bind the bootstrapper to application startup.

Setting up shell is easy – just change base class of AppBootstrapper from Bootstrapper to Bootstrapper<IShell>.

To make sure that our bootstrapper is run on application startup, we need to add it to application resources – it will then take care of everything else. To achieve this, update App.xaml as follows:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
			 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
			 xmlns:app="clr-namespace:Coproject"
			 x:Class="Coproject.App">
	<Application.Resources>
		<app:AppBootstrapper x:Key="AppBootstrapper" />
	</Application.Resources>
</Application>

We are done – make sure that Coproject.Web is your startup project and you can Run the application. You should see the text in ShellView.
image

Notice that we only told the application to use shell. There was no need to specify what view to show. This is caused by Caliburn.Micro and its ‘Convention over Configuration’ philosophy. It means that if you do the stuff right (this concerns mainly naming) , it will just work. We will see a lot more conventions of C.M later.

3. Add some style

Finally, let’s add some style sheets to make the application look better. In the Assets folder, create a new folder – Cosmopolitan and extract all seven files from the attached zip file to it.

Add references to these assemblies (make sure you choose the latest version if more than one is available):

System.Windows.Controls
System.Windows.Controls.Data
System.Windows.Controls.Data.Input
System.Windows.Controls.Data.DataForm.Toolkit
System.Windows.Controls.DataVisualization.Toolkit
System.Windows.Controls.Input
System.Windows.Controls.Input.Toolkit
System.Windows.Controls.Layout.Toolkit
System.Windows.Controls.Navigation
System.Windows.Controls.Toolkit

To register these resources in the application, edit App.xaml to look as follows (working with nested resource dictionaries might be tricky):

...
<Application.Resources>
	<ResourceDictionary>
		<ResourceDictionary.MergedDictionaries>
			<ResourceDictionary Source="Assets/Cosmopolitan/Styles.xaml"/>
			<ResourceDictionary Source="Assets/Cosmopolitan/CoreStyles.xaml"/>
			<ResourceDictionary Source="Assets/Cosmopolitan/SDKStyles.xaml"/>
			<ResourceDictionary Source="Assets/Cosmopolitan/ToolkitStyles.xaml"/>
			<ResourceDictionary Source="Assets/Cosmopolitan/Custom.xaml"/>
			<ResourceDictionary>
				<app:AppBootstrapper x:Key="AppBootstrapper" />
			</ResourceDictionary>
		</ResourceDictionary.MergedDictionaries>
	</ResourceDictionary>
</Application.Resources>
...

Finally, update content of ShellView:

<Border x:Name="LayoutRoot" Style="{StaticResource ContentBorderStyle}">
<Grid>
<Border Style="{StaticResource LeftBorderStyle}"/>
<ContentControl Style="{StaticResource LogoIcon}"/>

<TextBlock Text="Coproject" Style="{StaticResource ApplicationTitleStyle}" />
</Grid>
</Border>

And that is all for this part.
image

 

 

Tags: Silverlight, Caliburn, Ria, Coproject

2 Comments

  • Rob Eisenberg said

    Great article series! I just wanted to note that the call to Execute.InitializeWithDispatcher(); is not necessary in the bootstrapper. That should be taken care of in the base class. Though, it's not harmful to call it again.

  • Augustin Šulc said

    Good to know. I guess it wasn't there when I was writing AppBootstrapper for the first time (early stages of Caliburn.Micro) and I haven't checked if it is still needed since then. I've just removed it from Coproject source codes as well as from this post. Thank you!

Add a Comment