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:
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:
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.
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>