Description

Caliburn Microhttp://caliburnmicro.codeplex.com/ Introduction When my “Build Your Own MVVM Framework” talk was chosen for Mix10, I was excited to have the opportunity to show others what we had been doing in Caliburn in a simplified, but powerful way. After giving the talk, I received a ton of positive feedback on the techniques/framework that I had demonstrated. I was approached by several companies and individuals who expressed an interest in a more “official” version of what I had shown. That, coupled with the coming of Windows Phone 7, impressed upon me a need to have a more “lean and mean” framework. My vision was to take 90% of Caliburn’s features and squash them into 10% of the code. I started with the Mix sample framework, then used an iterative process of integrating Caliburn v2 features, simplifying and refactoring. I continued along those lines until I had what I felt was a complete solution that mirrored the full version of Caliburn v2, but on a smaller scale. Caliburn.Micro consists of one ~70k assembly (31k in XAP) that builds for WPF4, SL4 and WP7. It has a single dependency, System.Windows.Interactivity, which you are probably already using regularly in development. Caliburn.Micro is about 2,700 LOC total, so you can easily go through the whole codebase in a day and hold it in your head. That’s about 10% of the size of Caliburn v2, which is running around 27,000 LOC and is a lot harder to grasp in a short time. The best part is that I believe I was able to put something together that contains all of the features I consider most important in Caliburn. Here’s a brief list:   ActionMessages – The Action mechanism allows you to “bind” UI triggers, such as a Button’s “Click” event, to methods on your View-Model or Presenter. The mechanism allows for passing parameters to the method as well. Parameters can be databound to other FrameworkElements or can pass special values, such as the DataContext or EventArgs. All parameters are automatically type converted to the method’s signature. This mechanism also allows the “Action.Target” to vary independently of the DataContext and enables it to be declared at different points in the UI from the trigger. When a trigger occurs, the “message” bubbles through the element tree looking for an Action.Target (handler) that is capable of invoking the specified method. This is why we call them messages. The “bubbling” nature of Acton Messages is extremely powerful and very helpful especially in master/detail scenarios. In addition to invocation, the mechanism supports a “CanExecute” guard. If the Action has a corresponding Property or Method with the same name, but precede by the word “Can,” the invocation of the Action will be blocked and the UI will be disabled. Actions also support Coroutines (see below). That’s all fairly standard for existing Caliburn users, but we do have a few improvements in Caliburn.Micro that will be making their way into the larger framework. The Caliburn.Micro implementation of ActionMessages is built on System.Windows.Interactivity. This allows actions to be triggered by any TriggerBase developed by the community. Furthermore, Caliburn.Micro’s Actions have full design-time support in Blend. Code-centric developers will be happy to know that Caliburn.Micro supports a very terse syntax for declaring these ActionMessages through a special attached property called Message.Attach. Action Conventions – Out of the box, we support a set of binding conventions around the ActionMessage feature. These conventions are based on x:Name. So, if you have a method        called “Save” on your ViewModel and a Button named “Save” in your UI, we will automatically create an EventTrigger for the “Click” event and assign an ActionMessage for the “Save” method. Furthermore, we will inspect the method’s signature and properly construct the ActionMessage parameters. This mechanism can be turned off or customized. You can even change or add conventions for different controls. For example, you could make the convention event for Button “MouseMove” instead of “Click” if you really wanted. Binding Conventions – We also support convention-based databinding. This too works with x:Name. If you have a property on your ViewModel with the same name as an element, we will attempt to databind them. Whereas the framework understands convention events for Actions, it additionally understands convention binding properties (which you can customize or extend). When a binding name match occurs, we then proceed through several steps to build up the binding (all of which are customizable), configuring such details as BindingMode, StringFormat, ValueConverter, Validation and UpdateSourceTrigger (works for SL TextBox and PasswordBox too). Finally, we support the addition of custom behaviors for certain scenarios. This allows us to detect whether we need to auto-generate a DataTemplate or wire both the ItemsSource and the SelectedItem of a Selector based on naming patterns. Screens and Conductors – The Screen, ScreenConductor and ScreenCollection patterns enable model-based tracking of the active or current item, enforcing of screen lifecycles and elegant shutdown or shutdown cancellation in an application. Caliburn.Micro’s implementation of these patterns is an evolution of the one found in Caliburn and supports conducting any type of class, not just implementations of IScreen. These improvements are being introduced back into Caliburn. You’ll find that Caliburn.Micro’s screen implementation is quite thorough and even handles asynchronous shutdown scenarios with ease. Event Aggregator – Coming in at about 75LOC, Caliburn.Micro’s EventAggregator is simple yet powerful. The aggregator follows a bus-style pub/sub model. You register a message handler with the aggregator, and it sends you any messages you are interested in. You declare your interest in a particular message type by implementing IHandle<TMessage>. References to handlers are held weakly and publication occurs on the UI thread. Coroutines – Any action can optionally choose to return IResult or IEnumerable<IResult>, opening the door to a powerful approach to handling asynchronous programming. Furthermore, implementations of IResult have access to an execution context which tells them what ActionMessage they are executing for, what FrameworkElement triggered the messsage to be sent, what instance the ActionMessage was handled by (invoked on) and what the View is for that instance. Such contextual information enables a loosely-coupled, declarative mechanism by which a Presenter or View-Model can communicate with it’s View without needing to hold a reference to it at any time. ViewLocator – For every ViewModel in your application, Caliburn.Micro has a basic strategy for locating the View that should render it. We do this based on naming conventions. For example, if your VM is called MyApplication.ViewModels.ShellViewModel, we will look for MyApplication.Views.ShellView. Additionally, we support multiple views over the same ViewModel be attaching a View.Context in Xaml. So, given the same model as above, but with a View.Context=”Master” we would search for MyApplication.Views.Shell.Master. Of course, all this is customizable. WindowManager – This service provides a View-Model-centric way of displaying Windows (ChildWindow in SL and Window in WPF). Simply pass it an instance of the VM and it will locate the view, wrap it in a Window if necessary, apply all conventions you have configured and show the window. PropertyChangedBase and BindableCollection – What self respecting WPF/SL framework could go without a base implementation of INotifyPropertyChanged? The Caliburn.Micro implementation enables string and lambda-based change notification. It also ensures that all events are raised on the UI thread. BindableCollection is a simple collection that inherits from ObservableCollection<T>, but that ensures that all its events are raised on the UI thread as well.    Bootstrapper – What’s required to configure this framework and get it up and running? Not much. Simply inherit from Bootsrapper and add an instance of your custom bootstrapper to the Application’s ResourceDictionary. Done. If you want, you can override a few methods to plug in your own IoC container, declare what assemblies should be inspected for Views, etc. It’s pretty simple. Logging – Caliburn.Micro implements a basic logging abstraction. This is important in any serious framework that encourages Convention over Configuration. All the most important parts of the framework are covered with logging. Want to know what conventions are or are not being applied? Turn on logging. Want to know what actions are being executed? Turn on logging. Want to know what events are being published? Turn on logging. You get the picture. MVVM and MVP – In case it isn’t obvious, this framework enables MVVM. MVVM isn’t hard on it’s own, but Caliburn.Micro strives to go beyond simply getting it done. We want to write elegant, testable, maintainable and extensible presentation layer code…and we want it to be easy to do so. That’s what this is about. If you prefer using Supervising Controller and PassiveView to MVVM, go right ahead. You’ll find that Caliburn.Micro can help you a lot, particularly it’s Screen/ScreenConductor implementation. If you are not interested in any of the goals I just mentioned, you’d best move along. This framework isn’t for you. Just to be clear, this isn’t a toy framework. As I said, I really focused on supporting the core and most commonly used features from Caliburn v2. In fact, Caliburn.Micro is going to be my default framework moving forward and I recommend that if you are starting a new project you begin with the Micro framework. I’ve been careful to keep the application developer API consistent with the full version of Caliburn. In fact, the improvements I made in Caliburn.Micro are being folded back into Caliburn v2. What’s the good part about that? You can start developing with Caliburn.Micro, then if you hit edge cases or have some other need to move to Caliburn, you will be able to do so with little or no changes in your application. Keeping with the spirit of “Build Your Own…” I want developers to understand how this little framework works, inside and out. I’ve intentionally chosen Mercurial for source control, because I want developers to take ownership. While I’ve done some work to make the most important parts of the framework extensible, I’m hoping to see many Forks, each with their own customizations unique to their application’s needs. Obtain the Code There are two ways to get the code: Mercurial Caliburn.Micro uses Mercurial for source control. There are many Mercurial clients available that can be used with CodePlex. TortoiseHG is a popular Mercurial client that works as an extension in Windows Explorer. For more information please see Using TortoiseHG with CodePlex. If you are new to Mercurial, I highly recommend that you watch the free TekPub video on the subject here. Clone URL: https://hg01.codeplex.com/caliburnmicro Username: <your codeplex user name> Password: <same as your codeplex password> Direct download I only recommend this method if you are really uncomfortable with Mercurial. Simply navigate to the Source Code tab, click the link for the latest revision, then click the download link. Build the Code 1. 2. 3. 4. 5. Navigate to the folder where you downloaded the source code. Navigate to the "src" directory. Open Caliburn.Micro.sln. Press Ctrl-Shift-B (or use the Build menu) to build the solution. Locate the assemblies in the "bin" directory under the appropriate platform's project folder. You can find them both in the \src\Caliburn.Show(string.Silverlight\Bin\Release (or Debug) folder.Micro.Micro prefers a View-Model-First approach. //Don't do this in real life :) } } } Notice that the ShellViewModel inherits from PropertyChangedBase.Windows. let’s start there.dll and Caliburn. public string Name { get { return name. NotifyOfPropertyChange(() => CanSayHello).xaml” and clean up your “App. Delete “MainPage. NotifyOfPropertyChange(() => Name).Windows. } } } Since Caliburn.Micro.Micro.IsNullOrWhiteSpace(Name). It will come in handy :) . Name)).dll.Format("Hello {0}!".Hello”.Hello { using System. You don’t need a web site or test project. Add a reference to System.Basic Configuration. Use the following code for the implementation: namespace Caliburn.Hello { using System. Create your first VM and call it ShellViewModel. This is a base class that implements the infrastructure for property change notification and automatically performs UI thread marshalling.xaml. Actions and Conventions Open Visual Studio and create a new Silverlight 4 Application called “Caliburn.Interactivity. } } public void SayHello() { MessageBox.Windows. } } public bool CanSayHello { get { return !string.cs” so that it looks like this: namespace Caliburn.Micro. } set { name = value.Micro. public class ShellViewModel : PropertyChangedBase { string name. public partial class App : Application { public App() { InitializeComponent(). com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.Hello. The “root view mdoel” is a VM that Caliburn.App"> <Application.Hello { public class HelloBootstrapper : Bootstrapper<ShellViewModel> {} } There are two Bootsrappers available in Caliburn.xaml to match this: Silverlight <Application xmlns="http://schemas. This version allows you to specify the type of “root view model” via the generic type.MergedDictionaries> </ResourceDictionary> </Application.microsoft.Resources> </Application> WPF <Application xmlns="http://schemas. You should see something like this: .com/winfx/2006/xaml" xmlns:local="clr-namespace:Caliburn.Micro.Micro. You can use this tiny bit of code: namespace Caliburn.Resources> <ResourceDictionary> <ResourceDictionary. we need to place the HelloBootstrapper somewhere where it will be run at startup. let’s create the bootstrapper that will configure the framework and tell it what to do. run the application.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.MergedDictionaries> <ResourceDictionary> <local:HelloBootstrapper x:Key="bootstrapper" /> </ResourceDictionary> </ResourceDictionary. Next.Now that we have our VM.Resources> </Application> All we have to do is place a Caliburn.Resources and it will do the rest of the work.Micro bootstrapper in the Application. Now.Micro. change your App.Resources> <local:HelloBootstrapper x:Key="bootstrapper" /> </Application.Micro.microsoft.Hello" x:Class="Caliburn.Micro.Hello" x:Class="Caliburn.Hello. To do that.App"> <Application.Micro. Create a new class named HelloBootstrapper.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Caliburn.Micro will instantiate and use to show your application. microsoft. You should now see the UI: Typing something in the TextBox will enable the Button and clicking it will show a message: .Hello.Micro. So.Caliburn.Micro creates the ShellViewModel. Use the following xaml: <UserControl x:Class="Caliburn.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. let’s create a view.ShellView" xmlns="http://schemas. Create a new Silverlight User Control named ShellView.com/winfx/2006/xaml"> <StackPanel> <TextBox x:Name="Name" /> <Button x:Name="SayHello" Content="Click Me" /> </StackPanel> </UserControl> Run the application again.microsoft. but doesn’t know how to render it. The “CanSayHello” property is guarding access to the “SayHello” action by disabling the Button.MyViewModel. But. So. you can see that the TextBox with x:Name=”Name” is bound to the “Name” property on the VM.Views. Samples Caliburn Micro Hello . it would look for MyApp. These are the basics of Caliburn. it takes the FullName and removes “Model” from it.Micro’s ActionMessage and Conventions functionality.MyView. Essentially. Looking at the View and ViewModel side-by-side.Micro uses a simple naming convention to locate Views for ViewModels. There’s much more to show. given MyApp.Caliburn. next time I want to show how we can integrate an IoC container such as MEF.ViewModels. You can also see that the Button with x:Name=”SayHello” is bound to the method with the same name on the VM. ComponentModel. protected override void Configure() { container = CompositionHost.Composition. } protected override IEnumerable<object> GetAllInstances(Type serviceType) { return container. First.AddExportedValue(container).Generic.GetContractName(service Type)). System. contract)). System.AddExportedValue<IWindowManager>(new WindowManager()). In this part.Micro and demonstrated a couple of simple features related to Actions and Conventions. go ahead and grab the code from Part 1 or download the code for this article.Collections.GetExportedValues<object>(contract).Count() > 0) return exports. } } .Instance. System.AddExportedValue<IEventAggregator>(new EventAggregator()). We are going to use that as our starting point. Add two additional references: System.GetExportedValues<object>(AttributedModelServices. System.First().". let’s create a new Bootstrapper called MefBootstrapper.OfType<ComposablePartCatalog>() ) ).ComponentModel. } protected override void BuildUp(object instance) { container. but Caliburn. System.Primitives.ComponentModel. var batch = new CompositionBatch(). string key) { string contract = string.ComponentModel.Format("Could not locate any instances of contract {0}.Initialize( new AggregateCatalog( AssemblySource. We’ll use MEF for this example.Customizing The Bootstrapper In the last part we discussed the most basic configuration for Caliburn.GetContractName(serviceType) : key. if (exports. batch.Micro will work well with any container. Use the following code: using using using using using using System.ComponentModel.Linq.IsNullOrEmpty(key) ? AttributedModelServices. Those are the assemblies that contain MEF’s functionality. public class MefBootstrapper : Bootstrapper<IShell> { private CompositionContainer container. batch. Let’s begin by configuring our application to use an IoC container.Composition.Initialization. throw new Exception(string. } protected override object GetInstance(Type serviceType. I would like to explore the Bootstrapper class a little more. var exports = container.Hosting.Composition. batch.Select(x => new AssemblyCatalog(x)). container.Composition.SatisfyImportsOnce(instance).Compose(batch).1 Now.Composition and System. you don’t even need to worry about this.Instance. After we configure the container.Micro-specific services. you’ll need to make sure they get registered with your IoC container and the AssemblySoure. You can . we need to tell Caliburn. “GetInstance” and “GetAllInstances” are required by the framework. there are some other notable methods on the Bootstrapper. That is the purpose of the three overrides that follow. In future articles I will demonstrate at least one scenario where you may be tempted to access the ServiceLocator from a ViewModel. So.Instance? This is the place that Caliburn. Simply override SelectAssemblies like this: protected override IEnumerable<Assembly> SelectAssemblies() { return new[] { Assembly. After creating the container and providing it with the catalogs. such as customizing conventions. By default. You can just instantiate the container directly if you are working with . I’m creating an AggregateCatalog and populating it with AssemblyCatalogs. “BuildUp” is optionally used to supply property dependencies to instances of IResult that are executed by the framework. this is an extension point you need to remember.2 Besides what is shown above. what is AssemblySoure. I’m taking advantage of Silverlight’s CompositionHost to setup the CompositionContainer.Micro looks for Views. ServiceLocator is considered by many to be an anti-pattern.Instance when they are loaded. you should avoid using this directly in your application code. In this case. This gives us an opportunity to set up our IoC container as well as perform any other framework configuration we may want to do. So. } All you have to do is return a list of searchable assemblies. First. I make sure to add a few Caliburn. Then. if you are dynamically loading modules. the base class returns the assembly that your Application exists in. I also register the container with itself (just a personal preference). if all your views are in the same assembly as your application. one for each Assembly in AssemblySource. If you have multiple referenced assemblies that contain views.NET. we override the Configure method of the Bootstrapper class.Micro does provide ServiceLocator functionality through the Bootstrapper’s overrides and the IoC class. Word to the Wise While Caliburn.GetExecutingAssembly() }. I’ll also demonstrate some solutions. so I want them to be available for injection.Micro how to use it. Pulling from a container tends to obscure the intent of the dependent code and can make testing more complicated. Also. You can add assemblies to this at any time during your application to make them available to the framework.That’s all the code to integrate MEF. but there is also a special place to do it in the Bootstrapper. Those are pieces that I’m likely to take dependencies on elsewhere. The framework provides default implementations of both IWindowManager and IEventAggregator. . it’s just a marker interface. Application. #if SILVERLIGHT var view = ViewLocator. We need to add the IShell interface. then this is the method you want to override to change that behavior.Show(viewModel).Get<IWindowManager>().implementation is same as before. null).. The WPF version does the same thing by using the WindowManager class. ViewModelBinder. DisplayRootView is basically a convenience implementation for model-first development. try { windowManager = IoC. Here’s the code: public interface IShell { } Now. The last override. DisplayRootView.Bind(viewModel. locates the view for it and binds the two together. more or less.Get<TRootModel>(). Now that you understand all about the Bootstrapper. is unique. let’s get our sample working. It then makes sure to “activate” the VM if it implements the appropriate interface. we need to implement the interface and decorate our ShellViewModel with the appropriate MEF attributes: [Export(typeof(IShell))] public class ShellViewModel : PropertyChangedBase. But.RootVisual = view. In our case. } windowManager. view. If you don’t like it. IShell { .. #endif } The Silverlight version of this method resolves your root VM from the container..Activate(). } . null. #else IWindowManager windowManager. you would have some significant shell-related functionality baked into this contract. if (activator != null) activator.LocateForModel(viewModel. in a real application. var activator = viewModel as IActivate.override OnStartup and OnExit to execute code when the application starts or shuts down respectively and OnUnhandledException to cleanup after any exception that wasn’t specifically handled by your application code. perhaps because you prefer view-first MVVM. Let’s look at how it is implemented in Bootstrapper<TRootModel> protected override void DisplayRootView() { var viewModel = IoC. null). } catch { windowManager = new WindowManager(). I’m also excited to see that modern IoC containers as well as Caliburn. . you will only need to reference System. If you are using . I’m quite guilty of this myself. 2.That’s it! Your up and running with MEF and you have a handle on some of the other key extension point of the Bootstrapper as well.ComponentModel.Micro provide some very nice ways to avoid this situation.Composition. Samples Hello Mef Footnotes 1. but I’m trying to be more conscious of it.NET. 2 This bubbling nature of ActionMessage comes in handy in a number of interesting scenarios.Windows.Windows. why do I use the language “send a message” instead of “execute a method” when describing this functionality? That’s the interesting and powerful part.Interactivity for it’s trigger mechanism. it will check to see if that class also has either a property or a method named “CanSayHello. but you can create almost any kind of trigger imaginable or leverage some common triggers already created by the community. throwing an exception if no “handler” is found.Interactivit y" xmlns:cal="http://www.1 Perhaps the most common trigger is an EventTrigger.Triggers> </Button> </StackPanel> </UserControl> As you can see.Interactivity.Triggers> <i:EventTrigger EventName="Click"> <cal:ActionMessage MethodName="SayHello" /> </i:EventTrigger> </i:Interaction. Here’s the Xaml: <UserControl x:Class="Caliburn. But. then the framework will observe changes in that property and re-evaluate the guard accordingly. ActionMessage bubbles through the Visual Tree searching for a target instance that can handle it.” So.” If you have a guard property and your class implements INotifyPropertyChanged. where does that come from? Since we used a Model-First approach.Interactivity. It indicates that when the trigger occurs. there’s no visible indication of what that target will be. it set this up for us.assembly=System.microsoft.ShellView" xmlns="http://schemas. If a target is found. but does not have a “SayHello” method.microsoft. When a handler is found for the “SayHello” message.Micro-specific part of this markup. Master/Details being a key use case. we should send a message of “SayHello. the framework will continue to bubble until it finds one. Action Targets Now you’re probably wondering how to specify the target of an ActionMessage.Hello. Another important feature to note is Action guards. the Actions feature leverages System. of course.All About Actions We briefly introduced actions in Pt. We’ll discuss method guards in further detail below. when Caliburn. but there is so much more to know.Micro (hereafter CM) created the view and bound it to the ViewModel using the ViewModelBinder. To begin our investigation.TriggerBase to trigger the sending of an ActionMessage. ActionMessage is.org"> <StackPanel> <TextBox x:Name="Name" /> <Button Content="Click Me"> <i:Interaction. 1. So.Micro. the Caliburn. This means that you can use anything that inherits from System.com/winfx/2006/xaml" xmlns:i="clrnamespace:System.Windows.caliburnproject.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Anything that goes through the ViewModelBinder will have its action target set automatically. we’ll take our simple “Hello” example and see what it looks like when we explicitly create the actions rather than use conventions. you can set it . Looking at the markup above.Windows. The MEF configuration is the same as seen previously.org" cal:Bind. you can use the Bind. by adding an explicitly named contract: [Export("Shell". we’ll slightly alter how we are exporting our ShellViewModel.RootVisual = new ShellView().Target from the DataContext if you like.ViewFirst. using the attached property Action.yourself as well. It also sets the DataContext to the same value.microsoft.com/winfx/2006/xaml" xmlns:cal="http://www. instantiate the view ourselves and set it as the RootVisual (or call Show in the case of WPF). we’ve inherited from the non-generic Bootstrapper.caliburnproject.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. View First Let’s see how we would apply this to achieve MVVM using a View-First technique (gasp!) Here’s how we would change our bootstrapper: public class MefBootstrapper : Bootstrapper { //same as before protected override void DisplayRootView() { Application. typeof(IShell))] public class ShellViewModel : PropertyChangedBase. since you often want these two things to be the same. This gives you a nice way of doing View-First MVVM if you so desire. If you want Action. we will alter our view to pull in the VM and perform all bindings: <UserControl x:Class="Caliburn.TargetWithoutContext attached property instead. you can vary the Action. so I have left that out for brevity’s sake.Model="Shell"> <StackPanel> <TextBox x:Name="Name" /> <Button x:Name="SayHello" Content="Click Me" /> </StackPanel> </UserControl> . } //same as before } Because we are using View-First.Target. Simply use the Action.Model attached property in the same way. we simply override DisplayRootView. Setting this property positions an ActionMessage “handler” in the Visual Tree attached to the node on with you declare the property. However.Target is that you can set it to a System.ShellView" xmlns="http://schemas.Micro. In this scenario. Next. The only other thing that is changed is how the view gets created.Target set and you want Action/Binding Conventions applied as well. IShell { //same as before } Finally. One nice thing about Action.String and CM will use that string to resolve an instance from the IoC container using the provided value as its key.Current. microsoft.caliburnproject. etc.Format("Hello {0}!".Target and DataContext properties to the specified instance. we are now working with a completely POCO class.Target – Sets both the Action. but mainly I want to make clear the various ways that you can set targets for actions and the implications of using each technique.Windows.ComponentModel. using System.Composition. but with a bool return type. let’s switch back to our original ViewModel-First bootstrapper.microsoft. Now. we have added an input parameter to our SayHello method. String values are used to resolve an instance from the IoC container. Applies conventions to the view.Interactivity. [Export(typeof(IShell))] public class ShellViewModel : IShell { public bool CanSayHello(string name) { return !string. let’s take a look at another interesting aspect of ActionMessage: Parameters.Target property and the DataContext property to the specified instance.Show(string.Model – ViewModel-First – Locates the view for the specified VM instance and injects it at the content site.org"> <StackPanel> <TextBox x:Name="Name" /> .HelloParameters.Model attached property.Target and the DataContext. I thought it would be nice to show how View-First development is fully supported with CM. To see this in action.Set’s the Action.assembly=System. } public void SayHello(string name) { MessageBox.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. name)).IsNullOrWhiteSpace(name).TargetWithoutContext – Sets only the Action. Action Parameters Now. and begin by changing our ShellViewModel to look like this: using System. String values are used to resolve an instance from the IoC container. String values are used to resolve an instance from the IoC container.ShellView" xmlns="http://schemas.com/winfx/2006/xaml" xmlns:i="clrnamespace:System.Model – View-First .Target and DataContext and applies all conventions. Here’s a summary of the available attached properties:     Action.Interactivity" xmlns:cal="http://www.Windows. we changed our CanSayHello property into a method with the same inputs as the action. } } There are a few things to note here. Action. Finally.Micro.Target property to the specified instance. Bind. let’s have a look at the Xaml: <UserControl x:Class="Caliburn. sets the Action. Applies conventions to the view.Windows.Notice the use of the Bind. Sets the VM to the Action. This resolves our VM by key from the IoC container. Second. First. no INPC goop here. View. Value is a DependencyProperty. so all the standard binding capabilities apply to parameters. In addition to literal values and Binding Expressions.Triggers> </Button> </StackPanel> </UserControl> Our markup now has one modification: We declared the parameter as part of the ActionMessage using an ElementName Binding.Triggers> <i:EventTrigger EventName="Click"> <cal:ActionMessage MethodName="SayHello"> <cal:Parameter Value="{Binding ElementName=Name. Did I mention you can do all this in Blend? One thing that is nice about this is that every time the value of a parameter changes. Go ahead and run the application. Path=Text}" /> </cal:ActionMessage> </i:EventTrigger> </i:Interaction. You can have any number of parameters you desire. You’ll see that it behaves the same as in previous examples. there are a number of helpful “special” values . we’ll call the guard method associated with the action(CanSayHello in this case) and use its result to update the UI that the ActionMessage is attached to.<Button Content="Click Me"> <i:Interaction. microsoft.Micro. do you want to see something truly wicked? Change your Xaml back to this: <UserControl x:Class="Caliburn. } } . set. We’ll discuss conventions a lot more in the future. but they can be easily abused. Another scenario. public class Model { public Guid Id { get. Action Bubbling Now. Personally. but you should be happy to know that these conventions are case-insensitive and can even detect the before-mentioned “special” values. $dataContext – Passes the DataContext of the element that the ActionMessage is attached to. Word to the Wise Parameters are a convenience feature.that you can use with parameters. They are very powerful and can help you out of some tricky spots. I only use parameters in the simplest scenarios. but let’s do it with a shorthand syntax that is designed to be more developer friendly. $source – The actual FrameworkElement that triggered the ActionMessage to be sent. These allow you a convenient way to access common contextual information:    $eventArgs – Passes the Trigger’s EventArgs or input parameter to your Action.com/winfx/2006/xaml"> <StackPanel> <TextBox x:Name="Name" /> <Button x:Name="SayHello" Content="Click Me" /> </StackPanel> </UserControl> Running the application will confirm for you that CM’s conventions even understand ActionMessage parameters.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. as mentioned previously is Master/Detail operations.ShellView" xmlns="http://schemas. You must start the variable with a “$” but the name is treated in a case-insensitive way by CM. Note: This will be null for guard methods since the trigger hasn’t actually occurred.HelloParameters. One place where they have worked nicely for me is in login forms. This is very useful in Master/Detail scenarios where the ActionMessage may bubble to a parent VM but needs to carry with it the child instance to be acted upon. lets look at a simple Master/Detail scenario that demonstrates ActionMessage bubbling. We’ll start by adding a simple new class named Model: using System. Now. com/winfx/2006/xaml" xmlns:cal="http://www.Trigger/ActionMessage that you’ve seen . new Model { Id = Guid.Micro. private set. [Export(typeof(IShell))] public class ShellViewModel : IShell { public BindableCollection<Model> Items { get. The Message.And then we’ll change our ShellViewModel to this: using System.ShellView" xmlns="http://schemas.NewGuid() }. Notice that the Remove method takes a single parameter of type Model. let’s update the ShellView: <UserControl x:Class="Caliburn.NewGuid() } }.BubblingAction.NewGuid() }). } public ShellViewModel() { Items = new BindableCollection<Model>{ new Model { Id = Guid.microsoft. } public void Remove(Model child) { Items.Remove(child).caliburnproject.Attach The first thing to notice is that we are using a more Xaml-developer-friendly mechanism for declaring our ActionMessages.microsoft. Now.Add(new Model { Id = Guid. } public void Add() { Items.Attach="Add" /> </StackPanel> </UserControl> Message.NewGuid() }. using System.ItemTemplate> </ItemsControl> <Button Content="Add" cal:Message.ComponentModel.org"> <StackPanel> <ItemsControl x:Name="Items"> <ItemsControl. } } Now our shell has a collection of Model instances along with the ability to add or remove from the collection.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Button Content="Remove" cal:Message. new Model { Id = Guid.Attach="Remove($dataContext)" /> <TextBlock Text="{Binding Id}" /> </StackPanel> </DataTemplate> </ItemsControl.Composition.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.NewGuid() }. new Model { Id = Guid.Attach property is backed by a simple parser which takes its textual input and transforms it into the full Interaction. So. So.Triggers with ActionMessage.Attach declarations specify which event should send the message.Attach="SayHello(Name)" /> You can specify literals as parameters as well and even declare multiple actions by separating them with a semicolon: <Button Content="Let's Talk" cal:Message. Name. We’ve discussed the various ways to set the action target depending on your needs/architectural style: Action.Attach="[Event MouseEnter] = [Action Talk('Hello'. run the application.Text)]. If you haven’t already. It would look like this: <Button Content="Click Me" cal:Message. [Event MouseLeave] = [Action Talk('Goodbye'.Target.Attach.Text into a System.Attach="[Event Click] = [Action SayHello(Name. Message. If you work primarily in the Xaml editor and not in the designer.Attach="[Event Click] = [Action Remove($dataContext)]" /> Suppose we were to re-write our parameterized SayHello action with the Message. the parser will use the ConventionManager to determine the default event to use for the trigger. Bind. including the use of Parameters with literals.Text)]" /> But we could also leverage some smart defaults of the parser and do it like this: <Button Content="Click Me" cal:Message.Attach is not about cramming code into Xaml. It’s purpose is to provide a streamlined syntax for declaring when/what messages to send to the ViewModel.TargetWithoutContext. Notice that neither Message. element bindings3 and special values. We also saw an example of the bubbling nature of ActionMessage and demoed it using the streamlined Message.Model or View. All along the . Here’s what the full syntax for our Remove message would look like if we were declaring everything: <Button Content="Remove" cal:Message. for example. you’re going to like Message.previously. Action.Attach syntax. You can always be explicit of coarse. Name.Double parameter without any fear of a casting issue. In the case of Button.Model. we’ve discussed using Interaction. you can pump TextBox. If you leave off the event.Attach syntax.Text)]" /> WARNING Those developers who ask me to expand this functionality into a full-blown expression parser will be taken out back and…dealt with. it’s Click. Please don’t abuse this. Any doubts you had will hopefully be put to rest when you see that the message bubbling works as advertised :) Something else I would like to point out is that CM automatically performs type-conversion on parameters. Interactivity. Actually. . Caliburn v2. You may notice a shocking similarity in the markup. Now. One important detail about ElementName Bindings that I didn’t mention…It doesn’t work with WP7 currently.Windows. there’s one final killer feature of ActionMessage we haven’t discussed yet…Coroutines. if no handler is found. the framework will check the current DataContext to see if it has the requested method. parameter literals and special values still work as described along with all the rest of the ActionMessage features. the full version of Caliburn is not based on System. But. the infrastructure is not present to make this work in any sort of sane way. Samples Explicit Actions View First Parameters Bubbling Actions Footnotes 1. Caliburn’s trigger mechanism was around long before Blend’s. That said. 2. before an exception is thrown.0 will be migrated to use the Blend model in the near future. However. Due to the fact that WP7 is based on a version of Silverlight 3 which had an incomplete implementation of DependencyObject/DependencyProperty. Currently. 3.way we’ve looked at various examples of conventions in action too. that will have to wait until next time. This seamed like a reasonable fallback behavior. RegisterInstance(typeof(IPhoneService). its basic configuration. public class HelloWP7Bootstrapper : PhoneBootstrapper { PhoneContainer container. using Microsoft. "PageTwoViewModel".RegisterSingleton(typeof(PageTwoViewModel). but your going to find that while you may be an experienced WPF or Silverlight developer. typeof(MainPageViewModel)). null. container.Working with Windows Phone 7 Hopefully. Tombstoning. Launchers/Choosers and the AppBar. container. } protected override object GetInstance(Type service. } protected override void BuildUp(object instance) { container. null.RegisterPerRequest(typeof(TabViewModel). container.BuildUp(instance).RegisterSingleton(typeof(MainPageViewModel).Collections. particularly around Navigation (with ViewModels and Screen Activation). Bootstrapper Let’s start by getting our application configured correctly.Generic. but we inherit our bootstrapper from the PhoneBootstrapper class.Activator. new FrameAdapter(RootFrame)).RegisterInstance(typeof(INavigationService). protected override void Configure() { container = new PhoneContainer(). string key) { return container.Tasks. that doesn’t make WP7 development a snap. Microsoft still has a long ways to go in making “three screens and the cloud” a reality. In this part. typeof(TabViewModel)). PhoneNumberResult>(). } protected override IEnumerable<object> GetAllInstances(Type service) { return container. The new features in Caliburn. "MainPageViewModel".InstallChooser<PhoneNumberChooserTask. and how to take advantage of a few of its features. using System.Phone.GetInstance(service.InstallLauncher<EmailComposeTask>().Micro is. It’s unfortunate that I have to call out WP7. In previous parts we began by creating a bootstrapper and adding it to our Application. null. container. container. We do the same thing on WP7. new PhoneApplicationServiceAdapter(PhoneService)). key). container. } } . Here’s how the bootstrapper for our WP7 sample application looks: using System. typeof(PageTwoViewModel)).GetAllInstances(service). previous articles have you up to speed on what Caliburn.Resources.Activator. container. I want to talk about some WP7 specifics issues.Micro are specifically designed to address some of the shortcomings in WP7. I inherited from SimpleContainer to create PhoneContainer.xaml. which wraps the RootFrame and IPhoneService. I’m going to recommend . be sure to clear out all the code from the App.Important Note About App. Rather than fight this.Micro.HelloWP7.App" xmlns="http://schemas. In this case. which just adds some custom activation logic related to Launchers/Choosers. The RootFrame and PhoneService are created by the PhoneBootstrapper and added to the appropriate places. Of course. which wraps the native PhoneService.microsoft. First. I’ve written a simple container myself. so you can keep your App. You don’t get to control that and there are no extensibility points.cs If you create your WP7 application using a standard Visual Studio template.cs file except for the call to InitializeComponent in the constructor.Windows. public partial class App : Application { public App() { InitializeComponent(). When using CM's PhoneBootstrapper. the generated App.xaml. unlike the Silverlight version of the navigation framework.Resources> <local:HelloWP7Bootstrapper x:Key="bootstrapper" /> </Application. Additionally. We’ll dig into that a bit later. Like it or not (I don’t) the platform is going to create pages at will and the Frame control is going to conduct your application thusly.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. You can get the full code for SimpleContainer in the Recipes section of the CM Documentation.HelloWP7"> <Application.xaml.Resources> </Application> using System.Micro.xaml and App.cs files clean. Here’s what they should look like: <Application x:Class="Caliburn. you should know that WP7 enforces a View-First approach to UI at the platform level. } } INavigationService Let’s dig into what the FrameAdapter implementation of INavigationService does for you. The purpose of this code is to set up the root frame for the application and make sure everything gets initialized properly. The most important thing to note in this code is the use of PhoneBootstrapper and the registrations of two services: INavigationService. there are less IoC container options available. you don't need both. So.cs file will have a lot of code in it. that's what the bootstrapper's job is too (and in fact it does a few things better than the out-of-the-box code in addition to configuring CM).xaml.com/winfx/2006/xaml" xmlns:local="clr-namespace:Caliburn. For WP7. When Navigating Away From a Page     Detect whether the associated ViewModel implements the IGuardClose interface. IPhoneService There’s really not much to say about this. If the application is being tombstoned or closed. If the ViewModel implements the IActivate interface. use the ViewModelBinder to connect the Page to the located ViewModel. pass “true” to the Deactivate method indicating that the ViewModel should permanently close. If a VM is found. Look for properties on the VM that match the QueryString parameters and inject them. The behavior of the navigation service allows the correct VM to be hooked up to the page. If you prefer not to inherit from Screen. tombstoning or normal “closing” of the application (IDeactivate). but you can get started taking advantage of them with just the simple information I have provided. Now that we have the basic service explanations over with. In order to support the View-First approach. allows that VM to be notified that it is being navigated to (IActivate). This makes it easy for VMs to take a dependency on the functionality without being coupled to the actual phone service. but maintaining a Model-First composition strategy for the sub-components of those pages. I’ve enabled the FrameAdapter to hook into the native navigation frame’s functionality and augment it with the following behaviors: When Navigating To a Page     Use the new ViewModelLocator to conventionally determine the name of the VM that should be attached to the page being navigated to. you can implement any of the interfaces individually of coarse. I abstracted an interface for the built-in PhoneApplicationService and created a decorator. I haven’t discussed Screens and Conductors yet. Examine the Page’s QueryString. They provide a nice View-Model-Centric. call its Activate method. All these interfaces (and a couple more) are implemented by the Screen class. Pull that VM by key out of the container. call it’s Deactivate method. performing the necessary type coercion. I didn’t add any additional behavior. plug the FrameAdapter into your IoC container and implement whatever interfaces you care about on your VMs.1 If the ViewModel can close and implements the IDeactivate interface. testable and predictable way of responding to navigation without needing to wire up a ton of event handlers or write important application flow logic in the page’s code-behind. Simply follow the same naming conventions for View/ViewModels as normal. let’s create some Views and ViewModels . allows it to prevent navigation away from the current page (IGuardClose) and allows it to clean up after itself on navigation away. invoke the CanClose method and use its result to optionally cancel page navigation. If IGuardClose is implemented and the app is not being tombstoned or closed.embracing the View-First approach for Pages in WP7. Phone" xmlns:cal="clr-namespace:Caliburn. it will create the necessary Triggers/Actions behind the scenes and wire everything up. We’ll use that. Caliburn.ApplicationBar> </phone:PhoneApplicationPage> The most important thing to note here is the ApplicationBar.microsoft.MainPage" xmlns="http://schemas.assembly=Caliburn.Micro" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray. It (and the AppBarMenuItem) has a single additional property called "Message.Message text you would normally set on any other control.microsoft.Phone.HelloWP7.Phone" xmlns:shell="clrnamespace:Microsoft. Here’s what the MainPageViewModel looks like: using System.Micro" Style="{StaticResource PhoneTextNormalStyle}" /> <TextBlock Text="Main Page" Margin="-3.ApplicationBar> <shell:ApplicationBar IsVisible="True"> <shell:ApplicationBar. public class MainPageViewModel { readonly INavigationService navigationService.com/winfx/2006/xaml" xmlns:phone="clrnamespace:Microsoft.Buttons> </shell:ApplicationBar> </phone:PhoneApplicationPage.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.-8. .assembly=Microsoft.assembly=Microsoft. If you are.0" Style="{StaticResource PhoneTextTitle1Style}" /> </StackPanel> <Grid Grid. Since WP7's out-of-the-box AppBar buttons and menus do not support behaviors.0.Row="0" Margin="24.Controls.Micro defines its own.0.12"> <TextBlock Text="WP7 Caliburn.xaml for you and configures the application to use that as its default starting point.IsVisible="True"> <Grid Background="Transparent"> <Grid.RowDefinitions> <StackPanel Grid.Phone. At runtime CM's ViewModelBinder will inspect the view to see if you are using any of CM's custom AppBar components.png" Text="Page Two" Message="GotoPageTwo" /> </shell:ApplicationBar.Row="1"> <Button x:Name="GotoPageTwo" Content="Goto Page Two" /> </Grid> </Grid> <phone:PhoneApplicationPage.to get our navigation working and to demonstrate a few other features." You can use this to set the Action.Micro.Shell.24. The default WP7 template creates a MainPage. This allows you to use the full features of Actions directly with the WP7 AppBar. but change the Xaml to this: <phone:PhoneApplicationPage x:Class="Caliburn.Micro. Here we demonstrate using CM's AppBarButton.Buttons> <cal:AppBarButton IconUri="ApplicationIcon.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid. public MainPageViewModel(INavigationService navigationService) { this.navigationService = navigationService; } public void GotoPageTwo() { navigationService.Navigate(new Uri("/PageTwo.xaml?NumberOfTabs=5", UriKind.RelativeOrAbsolute)); } } Here’s what it would look like if we could run it at this point: It’s pretty simple, but there are a few things worth mentioning. The first is that our INavigationService is being injected through the constructor. This will happen when we navigate to the page and the VM is conventionally resolved and wired up. In this case we haven’t implemented any interfaces or inherited from any base classes. We don’t really care about lifecycle. Take a look at the GotoPageTwo method (conventionally wired to the big button and hooked into the AppBar by the ViewModelBinder). We are using the INavigationService to tell the phone to go to PageTwo.xaml. Remember the query string and have a look at the ViewModel for PageTwo: using System; using System.Linq; [SurviveTombstone] public class PageTwoViewModel : Conductor<IScreen>.Collection.OneActive { readonly Func<TabViewModel> createTab; readonly PivotFix<IScreen> pivotFix; public PageTwoViewModel(Func<TabViewModel> createTab) { this.createTab = createTab; pivotFix = new PivotFix<IScreen>(this); } public int NumberOfTabs { get; set; } protected override void OnInitialize() { Enumerable.Range(1, NumberOfTabs).Apply(x => { var tab = createTab(); tab.DisplayName = "Item " + x; Items.Add(tab); }); ActivateItem(Items[0]); } protected override void OnViewLoaded(object view) { pivotFix.OnViewLoaded(view, base.OnViewLoaded); } protected override void ChangeActiveItem(IScreen newItem, bool closePrevious) { pivotFix.ChangeActiveItem(newItem, closePrevious, base.ChangeActiveItem); } } The value of the the QueryString parameter “NumberOfTabs” is going to be injected into the property of the same name on the ViewModel. The OnInitialize method is part of the Activation lifecycle I mentioned above. If you notice in the bootstrapper’s configuration, PageTwoViewModel is a Singleton. OnInitialize will be called the first time this page is navigated to, not on any subsequent times, while OnActivate will be called every time this page is navigated to. These are properties of the Screen class from which this ViewModel ultimately inherits. Basically, all this method does is create a parameterized number of child ViewModels (TabViewModel) and then “Activates” the first one. The Items collection and Activation capabilities come from the Conductor base class. Just to remind you, I’m going to cover Screens and Conductors much more thoroughly in a future article. Also, take note of the SurviveTombstone attribute. We’ll get to that soon. One final thing to mention is that the current version of the phone SDK has a bug in the Pivot control when databinding the SelectedItem or SelectedIndex property. I've implemented a workaround and plugged it into the view model. Hopefully the next version of the SDK will resolve this issue and I will be able to remove this code. Next, let's look at the view that this is bound to: <phone:PhoneApplicationPage x:Class="Caliburn.Micro.HelloWP7.PageTwo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clrnamespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clrnamespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:Controls="clrnamespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <Grid Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Margin="24,24,0,12"> <TextBlock Text="WP7 Caliburn.Micro" Style="{StaticResource PhoneTextNormalStyle}" /> <TextBlock Text="Page Two" Margin="-3,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}" /> </StackPanel> <Controls:Pivot x:Name="Items" SelectedItem="{Binding ActiveItem, Mode=TwoWay}" Grid.Row="1"> <Controls:Pivot.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding DisplayName}" /> </DataTemplate> </Controls:Pivot.HeaderTemplate> </Controls:Pivot> </Grid> </phone:PhoneApplicationPage> It looks like this when running: The most interesting part of this view is the Pivot control. We've named it "Items" enabling it to bind to the Items collection on the ViewModel. Because it's an ItemsControl, it's ItemsSource will be bound properly. It may be your intuition that Pivot inherits from Selector as well, but it does not. So, the SelectedItem convention does not get applied. That's why we have to do that manually. We could fix this by creating a custom convention for Pivot. That's out of the scope of this article, but is demonstrated in the attached sample. Let’s take a look at that TabViewModel and it’s corresponding View. First the VM: using System; using System.Windows; using Microsoft.Phone.Tasks; [SurviveTombstone] public class TabViewModel : Screen, ILaunchChooser<PhoneNumberResult> { string text; [SurviveTombstone] public string Text { get { return text; } set { text = value; NotifyOfPropertyChange(() => Text); } } public void Choose() { TaskLaunchRequested(this, TaskLaunchEventArgs.For<PhoneNumberChooserTask>()); } public event EventHandler<TaskLaunchEventArgs> TaskLaunchRequested = delegate { }; public void Handle(PhoneNumberResult message) { MessageBox.Show("The result was " + message.TaskResult, DisplayName, MessageBoxButton.OK); //You should abstract this… } } Now you should be noticing some interesting tidbits here. I’ll be coming back to those in a minute. Let’s take a look at the last piece in our application’s UI, the view for these tabs: <UserControl x:Class="Caliburn.Micro.HelloWP7.TabView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <StackPanel Background="{StaticResource PhoneChromeBrush}"> <TextBlock x:Name="DisplayName" Margin="8 0 0 0" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" /> null)). I’ll show you what the PhoneContainer looks like. } public InstanceActivator Activator { get. This is code that gets a chance to execute whenever the container creates an instance of a class. By plugging in CMs InstanceActivator class. private set. For now. if someone is using your application and their friend calls them.Micro piece that helps us use Launchers/Choosers easier (discussed shortly). but it’s a powerful extensibility point. The phone’s OS remembers the page you were on and Navigates to it automatically (read more here). extract its DataContext and see if it has any attributes that implement ITombstone. Whenever the OS attempts to Tombstone your application. } } Tombstoning Now that we have all the pieces. Our Navigation system described above ensures that it gets hooked up with the right ViewModel. But.ActivateInstance(type. object[] args) { return Activator. you must ensure that that View/ViewModel is put back in the same state it was in before Tombstoning. type => GetInstance(type. your application will be Resurrected. That’s where the SurviveTombstone attribute you saw comes into play. I provided an overridable method to plug this in. I mentioned that I had modified my SimpleContainer to create a custom PhoneContainer. For my simple container. For example. Let’s add one more thing. A well designed WP7 application should restore itself to the exact state it was in before Tombstoning. It’s a bit different for each container. you can see how all that comes together in the image above. Caliburn. Pretty much every IoC Container has a place to plug in custom “activation” logic. } protected override object ActivateInstance(Type type. All I did was plug in a special Caliburn. When the call is over. We’ll get to the launcher/chooser bit a little later: using System. just to make our application complete before we dive into the really interesting parts. At any point in time a WP7 application has to expect that it will be shutdown by the OS. I can explain why I’ve gone through the trouble of setting all this up: Tombstoning. args)). This interface is used to serialize a class or property by flattening it and storing it into the .Micro will grab the current page. public class PhoneContainer : SimpleContainer { public PhoneContainer(WP7Bootstrapper bootStrapper) { Activator = new InstanceActivator(bootStrapper. we will make launchers/chooser so much easier for you. the phone will Tombstone your application in order to process the call.<TextBox x:Name="Text" /> <Button Content="Choose" x:Name="Choose" /> </StackPanel> </UserControl> Here we just have a TextBox bound to our Text property and a Button that will execute our Choose method. Hopefully.ActivateInstance(base. State property. Because each TabViewModel is attributed. click the “Choose” button (we are going to talk about choosers next). Lastly. The SurviveTombstone attribute is a simple out-of-the-box implementation of this interface that understands how to walk an object graph. you will see that this all works as expected. which is persisted by the OS solely for the purpose of Resurrection (if it occurs). After the chooser launches. This mechanism is customizable and entirely replaceable if you don’t like it.PhoneApplicationService. It has special knowledge of Conductors as well. it will be inspected for persistable properties. so that property on each child item will be persisted. After the debugger is attached.2 Here’s what you should see: Launchers and Choosers . If you look at the classes above. press the phone’s back button to cancel. by default. Second. You must decorate every class as well as the properties you want persisted. you can override the SelectInstancesToTombstone and SelectInstancesToResurrect methods on the PhoneBootstrapper to determine what should be persisted to begin with. so that it will persist their ActiveItem correctly and inspect their children. selecting a tab other than the first one and typing some text into the text box. The Text property is attributed. you can override the Tombstone and Resurrect methods on the PhoneBootstrapper to replace the built-in mechanism all together. If you download the the sample attached to this article and run the application. Because it is a Conductor. the index of its ActiveItem will be persisted and each of its Items will be traversed as well. This is an opt-in mechanism. you can create your own attributes that implement ITombstone to customize all or part of the process. you could grab several key VMs from your IoC and supply them to the persistence mechanism here if you like. Next. First. Try navigating to the second page. As I mentioned. your application goes back to page two with the proper tab selected and the text filled back in the text box. we only inspect the DataContext of the current page. you can see that the PageTwoViewModel is attributed. examine its properties and persist them. Immediately press F5 in Visual Studio to re-attach the debugger (remember that that chooser caused your application to be tombstoned). But. The chooser result is displayed as well. If it does. the official guidance is that you should have created that Chooser in the constructor of your code-behind file and wired up its completed event. But. are called Choosers.Choose method. it should work though. If you do it in your page’s constructor. your event handler is called immediately. As you can see. developers register the Launcher/Chooser types they intend to use with the Activator. You can see an example of this in the the TabViewModel. the activator will wire itself to the event defined by the interface. If the instance implements ILaunchTask or ILaunchChooser<TResult>. we attempt to call ILaunchChooser<TResult>. does that sound like it’s going to play nice with MVVM…or any attempt at decent software design? One thing is for sure.) Next. your application resurrects. When we find a match. it will navigate to that page. I’ve done some things to try to improve this and make it more friendly to UI architecture. it’s not clearly defined by their API. it should put itself back in the state it was before tombstoning.One unique aspect to WP7 development is Tasks. this part of the WP7 API is a disaster. When you re-wire to the event. the task instance is passed to the sender’s Configure method. Well. But should you handle the event in the constructor? Probably not. It’s also quite possible to wire it too late.Handle with the Chooser result at the “latest possible time. For example. you will see that I used the methods InstallLauncher and InstallChooser. When. It must be done at just the right time. They come in two flavors. are called Launchers. Since all instances come from the IoC container. in order to remove that from your pages. right now you should be wondering. it raises ILaunchTask. it won’t work. you probably at least need to wire for the Loaded event and delay handling of the callback until the UI is visible. Executing a task is as simple as creating the class and calling its Show method. If you look back at the sample’s bootstrapper. the infrastructure “records” the sender of the event so that it knows who to call back after resurrection. So. So. The infrastructure now calls Show on the task and your application is Tombstoned. Tasks that simply launch a built-in phone application without returning data. lets say you had the idea that you would create these Choosers as singletons at startup and just wire their events then and there. what is the right time? From what I can tell. So. your application gets Tombstoned. remember that Tombstoning thing? As soon as you call Show. Each instance that is created by the IoC container will be inspected by the Activator to see if it implements certain interfaces. You wired the event too early. But. the chooser will be re-created and the event re-wired. but the sample above does not. we inspect each instance that is created to see if it was the one we “recorded” as the sender of the task launch. (Some tasks have parameters which must be set. The following functionality is handled by the InstanceActivator and must be hooked into your IoC container for it to be enabled. such as PhoneNumberChooserTask. Here’s how it works:           First. You need to know how it works for your own benefit and to better appreciate what I am going to show you. such as EmailComposeTask. let me tell you how I really feel…the WP7 API for Choosers is a software design abomination. Now. Tasks that return data back to your application. you have to be very careful about when you create the Chooser and wire the event. When the VM wants to execute a task. Now. The infrastructure is trying to fake a cross-appinstance callback. The infrastructure is notified of the event and then determines if the sender implements IConfigureTask<TTask>. especially since your main UI isn’t even visible yet. so let me take a moment to explain.TaskLaunchRequested. “But what if I am using a Chooser? How do I get the results back into my application?” Well.” The latest possible time is evaluated based on . when the phone infrastructure tries to resurrect your application. ) If these conditions aren’t met. Hopefully the simple tombstoning mechanism and Launcher/Chooser abstraction will help you too.Micro without it. If it is IViewAware. That’s mostly because. If you run the sample. Samples Hello WP7 . we call Handle immediately. we call Handle after the class is activated (assuming it isn’t already activated. we call handle after the View’s Loaded event fires. Choosers are not very friendly to any sort of reasonable software design. you will see that this does indeed work and that the Chooser result is passed back to the correct VM after it’s View is shown. Please remember that all of this is completely optional. I’ve gone through a lot of WP7 specific stuff in this article. You can still leverage Caliburn. frankly. It sounds complicated. especially when combined with the SurviveTombstone attribute is a very natural developer experience. If it isn’t but implements IActivate. I think the FrameAdapter will prove to be the most useful and generalizable feature for building UI.the features of the class. However. I think the result of the custom Activator. yield instances of IResult from an Action2. } } First. There are two things necessary to take advantage of this feature in Caliburn.Hide(). then come back and resume execution where you left off. First we would probably want to show a “Loading” indicator.Micro: First.External. do some work on it and call another web service asynchronously. Say we had a Silverlight application where we wanted to dynamically download and show screens not part of the main package. then pause it’s execution on some statement.Composition. the third to hide the “Downloading” message and the fourth to show a new screen from the downloaded xap. yield return new ShowScreen("ExternalScreen"). coroutines are program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations. I mentioned that there was one more compelling feature of the Actions concept called Coroutines. the second to download the xap asynchronously."). After each yield statement. yield return Loader. representing the task you wish to execute. here’s what wikipedia1 has to say: In computer science. Accomplishing this with the standard event-driven async model is not a pleasant experience. [Export(typeof(ScreenOneViewModel))] public class ScreenOneViewModel { public IEnumerable<IResult> GoForward() { yield return Loader. then asynchronously download the external package.Coroutines. let’s say we have a ViewModel that needs to call a web service asynchronously. This is critical for using coroutines. Each of these yields is returning an instance of IResult.Generic.xap"). However. using System. Second. Coroutines are wellsuited for implementing more familiar program components such as cooperative tasks. iterators. yield return new LoadCatalog("Caliburn. The first is a result to show the “Downloading” indicator. implement the IResult interface on some class. we can (sort of) build them on top of iterators.ComponentModel. The problem…C# doesn’t implement coroutines natively. This technique is extremely powerful in task-based programming. If you haven’t heard that term before.Micro. Let’s make this more concrete.infinite lists and pipes. notice that the Action “GoForward” has a return type of IEnumerable<IResult>.Show("Downloading. next hide the “Loading” indicator and finally navigate to a particular screen inside the dynamic module... especially when those tasks need to run asynchronously. Here’s one way you can thing about it: Imagine being able to execute a method. the compiler will “pause” . this is a simple task to accomplish by using coroutines. For example. go do something else. it must then display the result in a modal dialog and respond to the user’s dialog selection with another asynchronous task. The body of the method has four yield statements. Fortunately.IResult and Coroutines Previously. Finally.Collections. Here’s what the code would look like if your first screen wanted to use coroutines to navigate to a dynamically loaded second screen: using System. then it needs to take the results of that. using System. CanExecute – A function that returns true if the Action can be invoked. while the second is asynchronous.the execution of this method until that particular task completes. Here’s the implementation: using System. Bearing that in mind. Target – The class instance on which the actual Action method exists. third and fourth tasks are synchronous. But the yield syntax allows you to write all the code in a sequential fashion. Source – The FrameworkElement that triggered the execution of the Action.Windows. Simply write your code in the “Execute” method and be sure to raise the “Completed” event when you are done. } And here’s an explanation of what all these properties mean:         Message – The original ActionMessage that caused the invocation of this IResult. Because coroutines occur inside of an Action.Windows. public object Target. readonly bool hide. Method – The MethodInfo specifying which method to invoke on the Target instance. using System. To understand a bit more how this works. Key Index: A place to store/retrieve any additional metadata which may be used by extensions to the framework. public MethodInfo Method. Here’s what the ActionResultContext looks like: public class ActionExecutionContext { public ActionMessage Message. public FrameworkElement Source. public Func<bool> CanExecute. false otherwise. View – The view associated with the Target. whether it be a synchronous or an asynchronous task. . have a look at the IResult interface: public interface IResult { void Execute(ActionExecutionContext context). public class Loader : IResult { readonly string message. } It’s a fairly simple interface to implement. public object EventArgs. The first. This allows the ViewModel a way to declaratively state it intentions in controlling the view without having any reference to a View or the need for interaction-based unit testing. EventArgs – Any event arguments associated with the trigger of the Action. public DependencyObject View.Controls. I wrote a naive Loader IResult that searches the VisualTree looking for the first instance of a BusyIndicator to use to display a loading message. public object this[string key]. event EventHandler<ResultCompletionEventArgs> Completed. preserving the original workflow as a much more readable and declarative structure. we provide you with an ActionExecutionContext useful in building UI-related IResult implementations. System.IsBusy = !hide. . play an animation. etc. place focus on a particular UI element based on VM properties rather than controls. public class LoadCatalog : IResult { static readonly Dictionary<string. show a VM-based modal dialog. } public static IResult Hide() { return new Loader(true). } public Loader(bool hide) { this. System. System. public static IResult Show(string message = null) { return new Loader(message). but by using a slightly different scenario.ComponentModel. DeploymentCatalog>(). while(view != null) { var busyIndicator = view as BusyIndicator.ComponentModel.Composition.public Loader(string message) { this.Composition.ReflectionModel. readonly string uri. DeploymentCatalog> Catalogs = new Dictionary<string. } public event EventHandler<ResultCompletionEventArgs> Completed = delegate { }.Parent as FrameworkElement. } public void Execute(ActionExecutionContext context) { var view = context. show a VMbased Popup at the user’s mouse position. Let’s look at how you might do that.Composition. System.View as FrameworkElement. } Completed(this.Generic. Just to list a few interesting things you could do with IResult implementations: show a message box. one of the biggest opportunities is calling web services.Collections. new ResultCompletionEventArgs()).Linq.Hosting.View? This opens up a lot of possibilities while maintaining separation between the view and the view model.IsNullOrEmpty(message)) busyIndicator. Of coarse.hide = hide. show File Save/Load dialogs. dynamically downloading a xap: using using using using using using System. System.message = message.BusyContent = message. if(busyIndicator != null) { if(!string. busyIndicator. } view = view.ComponentModel. break. } } See how I took advantage of context. catalog. out catalog)) Completed(this.GetPartType(part). else { catalog = new DeploymentCatalog(uri). Furthermore. you can write an action that assumes that if the code following the Dialog. } public void Execute(ActionExecutionContext context) { DeploymentCatalog catalog.Micro’s enumerator checks these properties after it get’s called back from each IResult. set WasCancelled on the event args. public bool WasCancelled. If there is either an error or WasCancelled is set to true.Instance. } } public event EventHandler<ResultCompletionEventArgs> Completed = delegate { }. } else Loader. Completed(this.Catalogs.Parts .Contains(assembly)) .Error == null) { Catalogs[uri] = catalog. } In case it wasn’t clear.Where(assembly => !AssemblySource. set. catalog.TryGetValue(uri. By doing this.Add(catalog).Error. Speaking of that.[Import] public AggregateCatalog Catalog { get. this sample is using MEF.DownloadAsync(). the user must have . You don’t really need to know a lot about MEF or DeploymentCatalog to get the takeaway.Add(x)). new ResultCompletionEventArgs()). Catalog. new ResultCompletionEventArgs { Error = e. You could check the result of that dialog. This is what enables the async pattern to work. } public LoadCatalog(string relativeUri) { uri = relativeUri. we are taking advantage of the DeploymentCatalog created for Silverlight 4. we stop execution.Select(part => ReflectionModelServices. here’s what that class looks like: public class ResultCompletionEventArgs : EventArgs { public Exception Error.Value.Instance.Assembly) . We also make sure to check the error and pass that along in the ResultCompletionEventArgs.Execute(context). if(Catalogs.Apply(x => AssemblySource. Just take note of the fact that we wire for the DownloadCompleted event and make sure to fire the IResult.DownloadCompleted += (s.Hide().Completed event in its handler. } Caliburn.Show executes. e) =>{ if(e. and if the user canceled it. catalog. }. WasCancelled = false }). Let’s say you create an IResult for the OpenFileDialog. You can use this to your advantage. public static ShowScreen Of<T>() { return new ShowScreen(typeof(T)). new ResultCompletionEventArgs()). I hope this gives some explanation and creative ideas for what can be accomplished with IResult. [Import] public IShell Shell { get. } public ShowScreen(string name) { this. } public ShowScreen(Type screenType) { this.screenType = screenType. you could use the same technique for the SaveFileDialog or any confirmation style message box if you so desired. We are going to be adding more common solutions such as this to that area in the coming months. Another thing you can do is create a series of IResult implementations built around your application’s shell.Get<object>(name) : IoC.ComponentModel. one of the things we added to the CM project site is a “Recipes” section. } public event EventHandler<ResultCompletionEventArgs> Completed = delegate { }. As a general rule.ActivateItem(screen). null). using System. set. This sort of technique can simplify the logic in such situations. This allows you to create them normally within your view models. So. readonly string name. However.selected a file. } } This bring up another important feature of IResult.IsNullOrEmpty(name) ? IoC. In this case.GetInstance(screenType. while still allowing them to take dependencies on application services. Shell.name = name. Here is its implementation: using System. you should avoid pulling things from the container directly. I think it is acceptable when done inside of infrastructure code such as a ShowScreen IResult. is that the original implementation was written by a CM user! Thanks janoveh for this awesome submission! As a side note. but in this case I chose to use the IoC static class internally. Completed(this. My favorite part of the LoadCatalog implementation shown above. You could also have your container injected.Composition. Be . Before CM executes a result. we depend on IShell. } public void Execute(ActionExecutionContext context) { var screen = !string. it passes it through the IoC. it will be a great place to check for cool plugins and customizations to the framework. public class ShowScreen : IResult { readonly Type screenType. Obviously. That is what the ShowScreen result used above does.BuildUp method allowing your container the opportunity to push dependencies in through the properties. Scrolling down to the section on C#…Caliburn was listed! Fun stuff. When I went to look up the “official” definition on wikipedia I was interested to see what it had to say about implementations in various languages. There’s a few other interesting things in there as well. 2. Samples Coroutines Note: Please be sure to run the sample as an out-of-browser application. .sure to check out the sample application attached. Footnotes 1. You can also return a single instance of IResult without using IEnuermable<IResult> if you just want a simple way to execute a single task. The shell may display lots of widgets as well. It’s particularly important if you want to leverage composition. it makes sure it gets deactivated. If a particular screen has complex activation logic. In simple scenarios. Screen Conductor Once you introduce the notion of a Screen Activation Lifecycle into your application. When you show a screen. it is different. Often times a screen has a lifecycle associated with it which allows the screen to perform custom activation and deactivation logic. it may be necessary to factor the ScreenActivator into its own class in order to reduce the complexity of the Screen. Screen Conductor and Screen Collection have more recently been codified by Jeremy Miller during his work on the book "Presentation Patterns" for Addison Wesley. You probably have a pretty good intuitive sense about this. then you switch to a tab containing an XML document. Presenter or ViewModel. The shell may display many different screens. a Screen could a be a UserControl. Conductors and Composition Actions. but the Screens and Conductors piece is probably most important to understand if you want your UI to be engineered well. you should remember that these are two separate roles. This is what Jeremy calls the ScreenActivator. It just activates/deactivates them. you need some way to enforce it. You might think of it as a stateful unit of work existing within the presentation tier of an application. If you are transitioning away from a screen. it doesn’t close documents when you switch from tab to tab. The terms Screen. Theory Screen This is the simplest construct to understand. which is already enforcing deactivation.Screens. This is the role of the ScreenConductor. Each one of those screens has custom activation/deactivation logic that enables it to setup/teardown the application toolbars such that they provide the appropriate icons based on the active screen. the ScreenActivator is often the same class as the Screen. That’s getting a little ahead of ourselves though. It’s independent from the application shell. There’s another scenario that’s important as well. can help out by implementing Graceful Shutdown. Suppose that you have a screen which contains unsaved data and someone tries to close that screen or even the application. the conductor makes sure it is properly activated. but all with the same activation/deactivation logic. First. In fact. Some screen examples might be a modal dialog for application settings. Coroutines and Conventions tend to draw the most attention to Caliburn. In the same way that your Screen might implement an interface for activation/deactivation. However. a code editor window in Visual Studio or a page in a browser.Micro. you will notice that the toolbar icons change. While these patterns are primarily used in CM by inheriting ViewModels from particular base classes. it may also implement some interface which allows the conductor to ask it “Can you close?” This brings up an important point: in some scenarios deactivating a screen is the same as closing a screen and in others. For example. take the Visual Studio code editor window. The ScreenConductor. its important to think of them as roles rather than as View-Models. You have to explicitly . For example. depending on your architecture. If you are editing a C# code file in one tab. some even at the same time. in Visual Studio. This is particularly important if you have an application with many different screens. but these are not part of any screen. let’s talk about what these things are in general. It all depends on your specific application’s architecture and it’s something you should think carefully about. That is what triggers the graceful shutdown logic.Micro Implementations These concepts are implemented in CM through various interfaces and base classes which can be used mostly1 to build ViewModels. after the document is closed. The caller will pass an Action<bool> to the CanClose method.. we can also solve the issue of deactivation vs. close. Anything that is in the ScreenCollection remains open. Caliburn. This interface provides an Activate method. allowing complex logic such as async user interaction to take place while making the close decision. All that would be dependent on whether or not it answers the question “Can you close?” positively. Add that to your IoC container and you’re off and running. etc. Of course. Let’s take a look at them: Screens In Caliburn. but it might also cause that page to close. By adding this piece of the puzzle. The implementer should call the action . Opening a new document would add it to the ScreenCollection and switch it to the active screen. Screen Collection In an application like Visual Studio. In an MDI-style application like VS. ScreenCollection could be a custom collection with special logic for maintaining the active screen. It has one method: CanClose. IGuardClose – Indicates that the implementer may need to cancel a close operation.Micro we have broken down the notion of screen activation into several interfaces:    IActivate – Indicates that the implementer requires activation. It also has two events: AttemptingDeactivation. but would remove it from the ScreenCollection.close the tab. an IsActive property and an Activated event which should be raised when activation occurs. but only one of those items is active at a time. which should be raised before deactivation and Deactivated which should be raised after deactivation. This interface has a Deactivate method which takes a bool property indicating whether to close the screen in addition to deactivating it. This method is designed with an async pattern. You could inherit from a TabControl and implement an IScreenConductor interface and build all the logic directly in the control. However. You could implement an IScreen interface on a custom UserControl or you could implement it as a POCO used as a base for Supervising Controllers. Closing a document would not only deactivate it. the conductor needs to decide which of the other items in the ScreenCollection should become the next active document. but would also have a ScreenCollection maintaining the list of currently opened screens or documents. Implementations There are lots of different ways to implement these ideas. in a navigation based application. the conductor would manage switching the active screen between members of the ScreenCollection. or it could just be a simple IList<IScreen>. navigating away from a page would definitely cause deactivation. you would not only have a ScreenConductor managing activation. IDeactivate – Indicates that the implementer requires deactivation. deactivation. Generally speaking. we have some convenience interfaces and base classes:     PropertyChangedBase – Implements INotifyPropertyChangedEx (and thus INotifyPropertyChanged). Also. What this all means is that you will probably inherit most of your view models from either PropertyChangedBase or Screen. it has an event which should be raised when a view is attached to the instance called ViewAttached. Because certain combinations are so common. This enables caching of complex views or even complex view resolution logic. Additionally.(2) IScreen – This interface composes several other interfaces: IHaveDisplayName. It adds an IsNotifying property which can be used to turn off/on all change notification. a NotifyOfPropertyChange method which can be called to raise a property change and a Refresh method which can be used to refresh all bindings on the object. we have a few others to help in creating consistency between presentation layer classes:      IHaveDisplayName – Has a single property called DisplayName INotifyPropertyChangedEx – This interface inherits from the standard INotifyPropertyChanged and augments it with additional behaviors. Also. IDeactivate. It has a GetView method which the framework calls before creating a view for the instance. IGuardClose and INotifyPropertyChangedEx Screen – Inherits from PropertyChangedBase and implements the IScreen interface. IActivate.when guard logic is complete. After initialization is complete. Finally. INotifyPropertyChangedEx and INotifyCollectionChanged IChild<T> – Implemented by elements that are part of a hierarchy or that need a reference to an owner.2 BindableCollection – Implements IObservableCollection<T> by inheriting from the standard ObservableCollection<T> and adding the additional behavior specified by INotifyPropertyChangedEx. enabling strongly-typed change notification. all property change events are automatically marshaled to the UI thread. In addition to these core lifecycle interfaces. false otherwise. CM’s default Screen implementation has a few additional features as well and makes it easy to hook into the appropriate parts of the lifecycle:  OnInitialize – Override this method to add logic which should execute only the first time that the screen is activated. It provides a lambda-based NotifyOfPropertyChange method in addition to the standard string mechanism. IsInitialized will be true. you would use Screen if you need any of the activation features and PropertyChangedBase for everything else. It has an AttachView method which is called by the framework when it binds the view to the instance. It has one property named Parent. this class ensures that all property change and collection change events occur on the UI thread. . IObservableCollection<T> – Composes the following interfaces: IList<T>. IChild<IConductor> and IViewAware are implemented. IViewAware – Implemented by classes which need to be made aware of the view that they are bound to. Pass true to indicate that the implementer can close. It indicates whether or not the activation was successful. GetConductedItems – Call this method to return a list of all the items that the conductor is tracking. just to re-iterate: if you need a lifecycle. ActivateItem – Call this method to activate a particular item. After activation is complete. If the screen is being controlled by a Conductor.” CloseItem – Call this method to close a particular item.3 INotifyPropertyChangedEx – This interface is composed into IConductor. Override this method to add custom guard logic.” ActivationProcessed – Raised when the conductor has processed the activation of an item. once you introduce lifecycle. TryClose – Call this method to close the screen. Use this if you are following a SupervisingController or PassiveView style and you need to work with the view. In Caliburn. this method attempts to close the view. After deactivation is complete. OnViewLoaded – Since Screen implements IViewAware. you need something to enforce it. If the conductor is using a “screen collection. In both scenarios the CanClose logic will be invoked and if allowed. inherit from Screen.” this returns all the “screens.     OnActivate – Override this method to add logic which should execute every time the screen is activated. it takes this as an opportunity to let you know when your view’s Loaded event is fired. IsActive will be true. Conductors As I mentioned above. with no constraints on the type. CanClose – The default implementation always allows closing. If the Screen is not controlled by a Conductor. this role is represented by the IConductor interface which has the following members:       ActiveItem – A property that indicates what item the conductor is currently tracking as active. IGuardClose and IChild<IConductor>. but exists independently (perhaps because it was shown using the WindowManager). otherwise inherit from PropertyChangedBase. OnDeactivate will be called with a value of true. OnDeactivate – Override this method to add custom logic which should be executed whenever the screen is deactivated or closed. This is also a place to put view model logic which may be dependent on the presence of a view even though you may not be working with the view directly. IDeactive. It will also remove it from the currently conducted items if the conductor is using a “screen collection.” otherwise this only returns ActiveItem. It will also add it to the currently conducted items if the conductor is using a “screen collection. The reason for this is that CM’s conductor implementations do not require the item being conducted to implement IScreen or any particular interface. You may have noticed that CM’s IConductor interface uses the term “item” rather than “screen” and that I was putting the term “screen collection” in quotes. In . The bool property will indicated if the deactivation is actually a close. IsActive will be false. So. it checks them individually for the following fine-grained interfaces: IActivate. Rather than enforcing the use of IScreen. it asks the Conductor to initiate the shutdown process for the Screen.Micro. each of the conductor implementations is generic. As the conductor is asked to activate/deactivate/close/etc each of the items it is conducting. Conducted items can be POCOs. Since the Conductor<T> does not maintain a “screen collection. This is a key feature of these implementations because it creates a composite pattern between screens and conductors. but this gives you the flexibility to use your own base class.4 When an item is closed and that item was the active item. that particular screen could inherit from Conductor<T>. Therefore. Be default.Collection. let’s say you are building a basic navigation-style application. Conductor<T>. If you need to change this behavior. let’s say that one of those screens was very complicated and needed to have a multi-tabbed interface requiring lifecycle events for each tab. the previous active item is deactivated only and it remains in the Items collection. So. Your shell would be an instance of Conductor<IScreen> because it shows one Screen at a time and doesn’t maintain a collection. you can override DetermineNextItemToActivate. the conductor must then determine which item should be activated next. But.” Since conductors in CM can conduct any type of class. Most of the time you’ll be fine with the DefaultCloseStrategy<T> that is provided automatically. One of those screens could even be a UserControl that implemented IScreen instead of a ViewModel if that’s what was required. one that works with a “screen collection” and one that does not. You can even have a conductor tracking heterogeneous items. but should you need to change things (perhaps IGuardClose is not sufficient for your purposes) you can set the CloseStrategy property on Conductor<T> to your own custom strategy. The actual logic for determining whether or not the conducted item can close can be complex due to the async nature of IGuardClose and the fact that the conducted item may or may not implement this interface. To close an item with this conductor. There are two very important details about both of CMs IConductor implementations that I have not mentioned yet. deactivation and closing of conducted items are not treated synonymously. We’ll look at the conductor without the collection first. some of which inherit from Screen and others that implement specific interfaces or none at all. When a new item is activated.” the activation of each new item causes both the deactivation and close of the previously active item.OneActive. This allows working with conductors generically through the interface as well as in a strongly typed fashion based on the items they are conducting. The second important detail is a consequence of . this collection is exposed through an IObservableCollection<T> called Items rather than Screens. Well. The shell need not be concerned with the complexity of the individual screen.Collection. the conductor delegates this to an ICloseStrategy<T> which handles this and tells the conductor the results of the inquiry. this is the item before the previous active item in the list.OneActive This implementation has all the features of Conductor<T> but also adds the notion of a “screen collection. As a result of the presence of the Items collection. they both inherit from Screen. I usually inherit conducted items from Screen.practice. you must explicitly call its CloseItem method. First. The Conductor<T> treats deactivation and closing synonymously. Out of the box CM has two implementations of IConductor. or to only implement the interfaces for the lifecycle events you care about on a perclass basis. Conductor<T> This simple conductor implements the majority of IConductor’s members through explicit interface mechanisms and adds strongly typed versions of the same methods which are available publicly. This turns out to be a very powerful feature. who is activating it? Well. Simple Navigation . For example. you may be wondering if/how you can leverage screens and conductors. Since all OOTB implementations of IConductor inherit from Screen it means that they too have a lifecycle and that lifecycle cascades to whatever items they are conducting. There’s one aspect about this that I’ve noticed frequently trips up developers. but can occasionally cause hair pulling. All your screens/conductors must be either rooted in a conductor or managed by the Bootstrapper or WindowManager to work properly. lifecycle isn’t magical. The WindowManager works in a similar way5 by acting a little like a conductor for the purpose of enforcing the lifecycle of your modal (and modeless . If you activate an item in a conductor that is itself not active. the WP7 version of CM has a FrameAdapter which hooks into the NavigationService. that item won’t actually be activated until the conductor gets activated. In these cases. understands the same fine-grained lifecycle interfaces that conductors do and ensures that they are called on your ViewModels at the appropriate points during navigation. I’ve been assuming a primarily ViewModelFirst approach to shell engineering. what about the your root view model? If it’s a conductor.WPF only) windows. it’s going to only be able to close if all of the items it conducts can close. if a conductor is deactivated. While the FrameAdapter is only part of the WP7 version of CM. So. which is set up by the PhoneBootstrapper. You can even cancel the phone’s page navigation by implementing IGuardClose on your ViewModel.the first. otherwise you are going to need to manage the lifecycle yourself. The Bootstrapper itself is not a conductor. but it understands the finegrained lifecycle interfaces discussed above and ensures that your root view model is treated with the respect it deserves. But the WP7 platform enforces a View-First approach by controlling page navigation. In order to make this play well with ViewModels. If you try to close a conductor. This makes sense when you think about it. View-First If you are working with WP7 or using the Silverlight Navigation Framework. So. This adapter. Quasi-Conductors Not everything in CM that can be a screen is rooted inside of a Conductor. The same is true of the SL Nav framework. that’s one of the jobs that the Bootstrapper performs. the Phone/Nav Framework acts like a conductor. So far. it’s ActiveItem will be deactivated as well. it should be easily portable to Silverlight should you wish to use it in conjunction with the Silverlight Navigation Framework. com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.Windows. As you can see from the project structure.Toolkit"> <tc:DockPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" tc:DockPanel.ShellView" xmlns="http://schemas.Windows.Previously. Let’s look at the ShellViewModel first. It inherits from Conductor<object> and is implemented as follows: public class ShellViewModel : Conductor<object> { public ShellViewModel() { ShowPageOne().microsoft. In order to keep this sample as simple as possible. we discussed the theory and basic APIs for Screens and Conductors in Caliburn.SimpleNavigation.com/winfx/2006/xaml" xmlns:tc="clrnamespace:System.assembly=System.Controls.microsoft. I’m not even using an IoC container with the Bootstrapper. This particular sample demonstrates how to set up a simple navigation-style shell using Conductor<T> and two “Page” view models.Micro. } } Here is the corresponding ShellView: <UserControl x:Class="Caliburn.Micro. } public void ShowPageOne() { ActivateItem(new PageOneViewModel()).Controls. Now I would like to walk through the first of several samples. we have the typical pattern of Bootstrapper and ShellViewModel. } public void ShowPageTwo() { ActivateItem(new PageTwoViewModel()).Dock="Top"> <Button x:Name="ShowPageOne" Content="Show Page One" /> <Button x:Name="ShowPageTwo" Content="Show Page Two" /> </StackPanel> <ContentControl x:Name="ActiveItem" /> </tc:DockPanel> </UserControl> . Show("Page Two Activated").Notice that the ShellViewModel has two methods. By naming it “ActiveItem” our data binding conventions kick in. For completeness.Micro. we actually set up a binding with CM’s custom attached property: View. Recall also. passing true to the Deactivate method to indicate that the view model should also be closed. yet simple ViewModel-First composition in the framework.PageTwoView" xmlns="http://schemas. Once that is complete.microsoft. it’s quite easy to get it rendering. //Don't do this in a real VM. then before the new instance is set.” Once the basic Conductor structure is in place. instead of binding to the Content property as we would in the other cases. If the item we are binding to is not a value type and not a string.microsoft. that if ActiveItem is already set to an instance.SimpleNavigation. } } Along with their views: <UserControl x:Class="Caliburn. So.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. All we have to do is place a ContentControl in the view. then we assume that the Content is a ViewModel. The convention for ContentControl is a bit interesting.OnActivate(). we pop the view into the ContentControl’s Content property.microsoft. This is all it takes to create a navigation application in Caliburn.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.SimpleNavigation.Micro. each of which passes a view model instance to the ActivateItem method. the conductor will then push it through the deactivation stage of the lifecycle.Micro. The ActiveItem of the conductor represents the “current page” and the conductor manages the transitions from one page to the other.PageOneView" xmlns="http://schemas. This is all done in a ViewModel-First fashion since its the conductor and it’s child view models that are driving the navigation and not the “views. The ShellView demonstrates this. This property causes CM’s ViewLocator to look up the appropriate view for the view model and CM’s ViewModelBinder to bind the two together. This single convention is what enables the powerful. the previous instance will be checked for an implementation of IGuardClose which may or may not cancel switching of the ActiveItem.microsoft. let’s take a look at the PageOneViewModel and PageTwoViewModel: public class PageOneViewModel {} public class PageTwoViewModel : Screen { protected override void OnActivate() { MessageBox. Assuming the current ActiveItem can close.com/winfx/2006/xaml"> <TextBlock FontSize="32">Page Two</TextBlock> </UserControl> .com/winfx/2006/xaml"> <TextBlock FontSize="32">Page One</TextBlock> </UserControl> <UserControl x:Class="Caliburn. base. Recall from our earlier discussion that ActivateItem is a method on Conductor<T> which will switch the ActiveItem property of the conductor to this instance and push the instance through the activation stage of the screen lifecycle (if it supports it by implementing IActivate).Model. Rather. even though your ViewModel has been activated. Here’s an important consequence of this that should be remembered: The activation is a ViewModel-specific lifecycle process and doesn’t guarantee anything about the state of the View. you should override Screen. So. if you have any activation logic that is dependent on the view being already loaded. it will just proceed to activate the new item. it’s view may not yet be visible. but the view for page two will not yet be visible. First it checks if the new item implements IChild<IConductor>. Remember that the conductors in CM don’t place any constraints on what can be conducted. The MessageBox will show when the activation occurs. when ActivateItem is called for PageTwoViewModel. Since it does not.I’ve intentionally kept this bare bones so that it’s easy to play around with and see how these pieces work together. it will check PageTwoViewModel to see if it implements IActivate.” As you can see.OnViewLoaded instead of/in combination with OnActivate. Finally. it will attempt to close it. it will first check PageOneViewModel to see if it implements IGuardClose. Many times. You will see this when you run the sample. Notice that PageOneViewModel is just a POCO. I have kept things pretty small and simple: . they check each instance for support of the various fine-grained lifecycle instances at the necessary times. the code in my OnActivate method will then run. Since it does not. it hooks up the hierarchical relationship. it will set the ActiveItem property on the conductor and raise the appropriate events. but here’s what it looks like: I’d like to point out a few final things. but PageTwoViewModel inherits from Screen. once again. Since Screen does. Remember. Next. Since Screen does. It’s not the slightest bit impressive. It will then check to see if it implements IDeactivate. Simple MDI Let’s look at another example: this time a simple MDI shell that uses “Screen Collections. Collection. but only keep one item active at a time.OneActive { int count = 1. public void OpenTab() { ActivateItem(new TabViewModel { DisplayName = "Tab " + count++ }). Let’s think .Collection. unique name. Note that. probably obvious). } } Since we want to maintain a list of open items. Clicking the “X” inside the tab will close that particular tab (also. different from our previous example. Clicking the “Open Tab” button does the obvious. Let’s dig into the code by looking at our ShellViewModel: public class ShellViewModel : Conductor<IScreen>. but this more closely mirrors what I would actually do in a real application.Here’s a screenshot of the application when it’s running: Here we have a simple WPF application with a series of tabs. The OpenTab method simply creates an instance of a TabViewModel and sets its DisplayName property (from IScreen) so that it has a human-readable. There’s not really a technical reason for this in this sample. I am actually constraining the type of the conducted item to IScreen. we use Conductor<T>.OneActive as our base class. Closing an Existing Item 1. If so. 4. determine which item to activate next and follow steps from “Opening an Additional Item. False is passed to indicate that it should be deactivated only.microsoft. Hopefully you can see some of the differences from a Conductor without a collection and understand why those differences are there. Checks the new item to see if it already exists in the Items collection. 3.Attach="CloseItem($dataContext)" /> </StackPanel> </DataTemplate> </TabControl. 2. If so.ItemTemplate> </TabControl> </DockPanel> </Window> .SimpleMDI. If not.Micro.Dock="Top" /> <TabControl x:Name="Items"> <TabControl. invoke with true to indicate that it should be deactivated and closed. Those are the main scenarios.ShellView" xmlns="http://schemas. Opening an Additional Item 1. it is added to the collection. Otherwise. Sets the new item as the ActiveItem.caliburnproject. 4. 2.microsoft.com/winfx/2006/xaml" xmlns:cal="http://www. 2. 3. Adds the item to the Items collection.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. the existing item is returned.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding DisplayName}" /> <Button Content="X" cal:Message.” 3.org" Width="640" Height="480"> <DockPanel> <Button x:Name="OpenTab" Content="Open Tab" DockPanel. Checks the item for IActivate and invokes it if present. Let’s see how the ShellView renders: <Window x:Class="Caliburn. If not. Checks to see if the closing item is the current ActiveItem. Remove the item from the Items collection. Passes the item to the CloseStrategy to determine if it can be closed (by default it looks for IGuardClose). the action is cancelled.through the logic for the interaction between the conductor and its screens in several key scenarios: Opening the First Item 1. and not closed. Sets the item as the ActiveItem. Checks the item for IActivate and invokes if present. Checks to see if the closing item is IDeactivate. Checks the current ActiveItem for IDeactivate and invokes if present. As you can see we are using a WPF TabControl. CM’s conventions will bind its ItemsSource to the Items collection and its SelectedItem to the ActiveItem. Put these in a DockPanel and use some naming conventions and you will have the same affect as a TabControl.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas. Create a couple of custom view models and views. Add it to the ShellViewModel and ensure that it is rendered in the ShellView (remember you can use a named ContentControl for this). It will also add a default ContentTemplate which will be used to compose in the ViewModel/View pair for the ActiveItem. Conventions can also supply an ItemTemplate since our tabs all implement IHaveDisplayName (through Screen). Add an IoC container and register the ToolBarViewModel as a singleton. but that’s not the case for our next sample. Take the SimpleMDI sample and the SimpleNavigation sample and compose them together. In preparation.SimpleMDI { public class TabViewModel : Screen {} } <UserControl x:Class="Caliburn. try using a horizontal ListBox as the tabs and a ContentControl as the tab content.Micro. For completeness. Hybrid . You wouldn’t really do something like this in a real application. We’ll talk more in depth about conventions in a later article. HINT: Use the events.com/winfx/2006/xaml"> <StackPanel Orientation="Horizontal"> <TextBlock Text="This is the view for "/> <TextBlock x:Name="DisplayName" /> <TextBlock Text=".microsoft. Either add the MDI Shell as a PageViewModel in the Navigation Sample or add the Navigation Shell as a Tab in the MDI Sample.TabView" xmlns="http://schemas.SimpleMDI. Wire things up so that you can open different view models in the conductor." /> </StackPanel> </UserControl> I’ve tried to keep it simple so far. Confirm that you see the correct view in the tab control when each view model is activated. Create a toolbar view model.microsoft. have the ToolBarViewModel injected into each of the TabViewModels. Instead. you might want to at least think through or try to do these things:     Get rid of the generic TabViewModel. here are the trivial implementations of TabViewModel along with its view: namespace Caliburn. Unfortunately. Write code in the TabViewModel OnActivate and OnDeactivate to add/remove contextual items from the toolbar when the particular TabViewModel is activated. but I’ve opted to override that by supplying my own to enable closing the tabs.Micro. BONUS: Create a DSL for doing this which doesn’t require explicit code in the OnDeactivate override. Rebuild this sample in Silverlight. Silverlight’s TabControl is utterly broken and cannot fully leverage databinding. Next. have a look at this short video for a brief visual explanation. Orders. It’s better if you take the time to look through it and figure out how things work yourself. then I might break that down into those areas. I do want to point out a few interesting implementation details.Micro is that they are an . such as Views and ViewModels. let’s look at how it’s put together. now that you’ve seen what it does. Ok. If I have a complex feature. I’m not going to go line-by-line through this sample. But. I’ve chosen to organize the project by features: Customers. ViewModel Composition One of the most important features of Screens and Conductors in Caliburn. As you can see from the screenshot. In most projects I prefer to do something like this rather than organizing by “technical” groupings.This sample is based loosely on the ideas demonstrated by Billy Hollis in this well-known DNR TV episode. etc. Rather than take the time to explain what the UI does. Settings. implementation of the Composite Pattern. Generally speaking. The first shows the application with the CustomersWorkspace in view. so you can see how the UI is composed. lets look at two screenshots. To see how composition plays a role in this particular sample. Editing a Customer’s Address Editing a Customer’s Address (3D Breakout) . The second screen is the same. composition is one of the most important aspects of object oriented programming and learning how to use it in your presentation tier can yield great benefits. editing a specific Customer’s Address. making them easy to compose together in different configurations. but with its View/ViewModel pairs rotated three-dimensionally. Collection. The view for the DialogConductor overlays the CustomerView. not anything else). In the state depicted above. thus the modal dialog is displayed with the AddressView and the underlying CustomerView is disabled. The details view also has a TransitioningContentControl bound to the CustomerWorkspaceViewModel’s ActiveItem. the ShellViewModel is a Conductor<IWorkspace>. we are showing the details view. Header and bottom Dock. the activated workspace is injected and it’s view is shown at that location. In the screenshot above. which is a property on CustomerViewModel. The Dock has buttons. It is visually represented by the Window Chrome. the DialogConductor’s ActiveItem is set to an instance of AddressViewModel. Multiple Views over the Same ViewModel . one for each IWorkspace that is being conducted. It so happens that the CustomerWorkspaceViewModel inherits from Conductor<CustomerViewModel>.Collection. This is managed by an instance of DialogConductor. thus causing the current CustomerViewModel to be composed in along with its view. The entire shell framework used in this sample works in this fashion and is entirely extensible simply by implementing IWorkspace. The CustomerViewModel has the ability to show local modal dialogs (they are only modal to that specific custom record. Since the ShellView has a TransitioningContentControl bound to the ActiveItem.OneActive. In this case. but is only visible (via a value converter) if the DialogConductor’s ActiveItem is not null. CustomerViewModel and SettingsViewModel are two different implementations of this interface you can dig into.OneActive. it’s the CustomerWorkspaceViewModel that is active. Clicking on a particular button makes the Shell activate that particular workspace.In this application. There are two contextual views for this ViewModel (see below). you can simply customize the ViewLocator.Customers. but Caliburn. In the case of this sample. } public IEnumerable GetConductedItems() { return ActiveItem != null ? new[] { ActiveItem } : new object[0]. Normally.Customers.CustomersWorkspace. Let’s take a look at that: [Export(typeof(IDialogManager)).Context="{Binding State. If that doesn’t work for you. I needed a dialog manager that could be modal to a specific part of the application without affecting other parts. Mode=TwoWay}" cal:View. where it displays all open CustomerViewModels. minus the words “View” and “Model”.HelloScreens. IDialogManager. private set.NonShared)] public class DialogConductorViewModel : PropertyChangedBase. a search UI and a New button. it’s going to look for Caliburn. In order for CM to find these contextual views. but the content region is the most noteworthy part of the view.Model="{Binding}" Style="{StaticResource specialTransition}"/> There is a lot of other Xaml surrounding this to form the chrome of the CustomerWorkspaceView. This allows us to dynamically change out views based on the value of that property.Micro provides the developer with default implementations of IScreen and IConductor. Here’s an example from the default CustomerWorkspaceView: <clt:TransitioningContentControl cal:View. we get a nice transition whenever the view changes. Custom IConductor Implementation Although Caliburn. This is supported by setting the View. PartCreationPolicy(CreationPolicy.Context attached property on the View/ViewModel’s injection site. the default Conductor<T> would work. where it displays the currently activated CustomerViewModel along with it’s specific view (composed in). but I discovered I needed to fine-tune shutdown sequence. IConductor { readonly Func<IMessageBox> createMessageBox. } public void ActivateItem(object item) { ActiveItem = item as IScreen. .You may not be aware of this.Context attached property to the State property on the CustomerWorkspaceViewModel. [ImportingConstructor] public DialogConductorViewModel(Func<IMessageBox> messageBoxFactory) { createMessageBox = messageBoxFactory. Because this is all hosted in the TransitioningContentControl.Micro.CustomersWorkspaceViewModel. you need a namespace based on the ViewModel name. For example. Notice that we are binding the View.HelloScreens. It’s easy to implement your own.LocateForModelType func.Micro can display multiple Views over the same ViewModel. with some Views named corresponding to the Context. This technique is used to switch the CustomerWorkspaceViewModel from a “Master” view. to a “Detail” view. } public IScreen ActiveItem { get. so I implemented my own.Micro.Detail That’s the out-of-the-box naming convention. when the framework looks for the Detail view of Caliburn. ActivationProcessed(this. box.ActiveItem { get { return ActiveItem. } public void CloseItem(object item) { var guard = item as IGuardClose. }). I also created a couple of simple methods for showing dialogs and message boxes which are exposed through the IDialogManager interface. public void ShowDialog(IScreen dialogModel) { ActivateItem(dialogModel).Ok.Message = message. }.Activate().Parent = this.Deactivate(true). Mainly. Action<IMessageBox> callback = null) { var box = createMessageBox(). as demonstrated . box. } object IConductor. } set { ActivateItem(value). But I chose to do this in order to represent the role this class was playing in the system and keep things as architecturally consistent as possible. if(child != null) child. The implementation itself is pretty straight forward. } } public event EventHandler<ActivationProcessedEventArgs> ActivationProcessed = delegate { }. MessageBoxOptions options = MessageBoxOptions. } else CloseActiveItemCore(). ActivateItem(box). } public void ShowMessageBox(string message.Deactivated += delegate { callback(box).var child = ActiveItem as IChild<IConductor>. I didn’t actually need to implement IConductor to make this work (since I’m not composing it into anything). NotifyOfPropertyChange(() => ActiveItem). ActivateItem(null). a conductor needs to make sure to Activate/Deactivate its items correctly and to properly update the ActiveItem property. if(callback != null) box. } } Strictly speaking. Success = true }). if(ActiveItem != null) ActiveItem. oldItem.DisplayName = title ?? "Hello Screens". if(guard != null) { guard.Options = options. string title = null. } void CloseActiveItemCore() { var oldItem = ActiveItem. This class is registered as NonShared with MEF so that each portion of the application that wants to display local modals will get its own instance and be able to maintain its own state. box. new ActivationProcessedEventArgs { Item = ActiveItem.CanClose(result => { if(result) CloseActiveItemCore(). mainWindow. IShell { readonly IDialogManager dialogs. } The ShellViewModel inherits this functionality through its base class Conductor<IWorkspace>.CanClose(result => { if(result) { actuallyClosing = true. [ImportingConstructor] public ShellViewModel(IDialogManager dialogs. in the Bootstrapper we just override DisplayRootView and wire Silverlight’s MainWindow. mainWindow. e.DisplayRootView().Get<IShell>(). if (Application.OneActive.Closing += MainWindowClosing. Since IShell inherits IGuardClose.OnUIThread(() => { var shell = IoC.Closing event to call IShell.MainWindow. } } void MainWindowClosing(object sender. Execute. Custom ICloseStrategy Possibly one of the coolest features of this sample is how we control application shutdown. Items. Here’s how we plug in our custom strategy: [Export(typeof(IShell))] public class ShellViewModel : Conductor<IWorkspace>.Cancel = true. }). ClosingEventArgs e) { if (actuallyClosing) return.Collection. } }). shell.CanClose: protected override void DisplayRootView() { base.OneActive.dialogs = dialogs.with the CustomerViewModel discussed above. } } } . Since all the built-in conductors have a CloseStrategy. we can create conductor specific mechanisms for shutdown and plug them in easily. [ImportMany]IEnumerable<IWorkspace> workspaces) { this. } public IDialogManager Dialogs { get { return dialogs.Close().AddRange(workspaces). CloseStrategy = new ApplicationCloseStrategy().IsRunningOutOfBrowser) { mainWindow = Application.Collection. Select(x => x. Check each IWorkspace to see if it is an IConductor.WasCancelled) Evaluate(!e. sequential.Current. The CustomerViewModel and OrderViewModel use this mechanism to display a modal dialog if there . if (!enumerator. else { var current = enumerator. IEnumerable<IWorkspace>> callback. 5. It will return null if there is no task.GetConductedItems() . 2. IEnumerable<IWorkspace>> callback) { enumerator = toClose. } else Evaluate(true).OfType<IHaveShutdownTask>() . 3. 7. If all IResults complete successfully. Action<bool. var sequential = new SequentialResult(tasks. Retrieve the shutdown task by calling GetShutdownTask.MoveNext() || !result) callback(finalResult. Evaluate(finalResult). } } } The interesting thing I did here was to reuse the IResult functionality for async shutdown of the application.GetShutdownTask()) . var conductor = current as IConductor.WasCancelled). Action<bool. if (conductor != null) { var tasks = conductor. Since the shutdown task is an IResult. The IResult can set ResultCompletionEventArgs.callback = callback.Execute(new ActionExecutionContext()).GetEnumerator(). }. the application will be allowed to close. pass all of these to a SequentialResult and begin enumeration. Continue through all workspaces until finished or cancellation occurs. 4. grab all the conducted items which implement the application-specific interface IHaveShutdownTask. sequential. bool finalResult.Where(x => x != null). Here’s how the custom strategy uses it: 1. finalResult = true. public void Execute(IEnumerable<IWorkspace> toClose.WasCanceled to true to cancel the application shutdown. e) => { if(!e.And here’s the implementation of that strategy: public class ApplicationCloseStrategy : ICloseStrategy<IWorkspace> { IEnumerator<IWorkspace> enumerator. 6. new List<IWorkspace>()). this. } void Evaluate(bool result) { finalResult = finalResult && result.Completed += (s.GetEnumerator()). If true. so filter those out. then activation of the new item would not succeed. This would work quite nicely for that too. If you are working with these classes without running the Bootstrapper (as would be the case with testing). If activation would cause the closing of the ActiveItem.OnUIThread utility method. a default action is used which does not do any marshalling. Even though these classes do automatic UI thread marshalling. but since the base implementations Screen. you could easily implement IConductor on top of a docking window manager or some other complex control or service. using these classes as view models will suffice. Under the covers. these classes use CM’s Execute. suppose you had some long running process that you wanted to prevent shutdown of the application.Collection. Additionally. Conductor<T> and Conductor<T>. such as it would with Conductor<T>. and the active item’s CanClose returned false. Or if the item inherits from Screen. For example. which actually just calls Parent. 3. 4.Micro code depends only on the interfaces. In fact the WPF version of the Bootstrapper uses the WindowManager internally to show your MainWindow. it’s easy to get a reference to the view and take one of these alternative approaches. 2.OneActive all implement IViewAware. I’ll have a whole article on the WindowManager coming soon. since all Caliburn. Normally. you can call the Screen’s TryClose method. Samples Simple Navigation Simple MDI Hybrid Footnotes 1. they are still safe to use in a unit test. . you could also use this for any number of async tasks.is dirty data. 5.CloseItem passing itself as the item to be closed. These classes can also be used very easily to create a SupervisingController or even a PassiveView design if so desired. This method calls a custom action to do the thread marshalling which is setup by the Bootstrapper. But. The current item would remain active. In ViewModel-First. Now. UIElement> LocateForModelType = (modelType.CustomerView.Remove(viewTypeName. if(context != null) { viewTypeName = viewTypeName. that’s pretty straight forward. If you are going to use conventions. we make an assumption that you are using the text “ViewModel” in the naming of your VMs. CM uses a simple naming pattern to find a UserControl1 that it should bind to the ViewModel and display.". That’s why CM’s conventions are fully customizable and can even be turned off completely if not desired. If a context (typically a string or an enum) is provided. }. we create an instance (or get one from the IoC container if it’s registered) and return it to the caller. displayLocation.Customer. string.FirstOrDefault(). Some people love conventions and some hate them.Master. we generate a view with an appropriate “not found” message. so we just change that to “View” everywhere that we find it by removing the word “Model”.Instance. To do this." + context. Once we have the name.Format("{0} not found.4.CustomerViewModel would become Views. This convention affects any ViewModel-First areas of your application. we do a further transformation of the name.All About Conventions One of the main features of Caliburn.FullName. We search any assembly you have exposed to CM as searchable via AssemblySource. object.Micro is manifest in its ability to remove the need for boiler plate code by acting on a series of conventions. Or if you are organizing your application by feature: CustomerManagement. what is that pattern? Let’s just take a look at ViewLocator. So ViewModels. To derive the view. DependencyObject. So.FullName == viewTypeName select type). So.LocateForModelType to find out: public static Func<Type. That’s the subject of this article. it’s good to know what those conventions are and how they work. based on that value.Replace("Model".Length . Let’s ignore the “context” variable at first. we then search for types with that name. This is how CM supports multiple Views over the same ViewModel. . back to that “context” value. } var viewType = (from assmebly in AssemblySource.2 If we find the type. viewTypeName) } : GetOrCreateViewType(viewType). This has the effect of changing both type names and namespaces.CustomerViewModel becomes CustomerManagement. return viewType == null ? new TextBlock { Text = string. If we don’t find the type. Hopefully. and since they are ON by default.Empty). 4).GetExportedTypes() where type.CustomerViewModel would become Views. context) =>{ var viewTypeName = modelType. given a context of “Master” our ViewModels. viewTypeName = viewTypeName + ". we have an existing ViewModel that we need to render to the screen.Instance from type in assmebly.CustomerView. This transformation effectively assumes you have a folder (namespace) for the different views by removing the word “View” from the end and appending the context instead. View Resolution (ViewModel-First) Basics The first convention you are likely to encounter when using CM is related to view resolution. If not. We use a similar naming convention for this scenario as we did with view location. It is always called indirectly through ViewLocator. the locator is invoked to see how that composed ViewModel should be rendered at that location in the UI. it will call it’s GetView method to see if you have a cached view or if you are handling View creation explicitly.Model attached property. Let’s take a look at ViewModelLocator. If you just want to add to the existing behavior. GetOrCreateViewType will call InitializeComponent on your View (if it exists).LocateForModelType. LocateForModel takes an instance of your ViewModel and returns an instance of your View. The first place is in Bootstrapper<T>. In Silverlight this results in the setting or your RootVisual. object> LocateForViewType = viewType =>{ var typeName = viewType. which brings me to… The second place the ViewLocator is used is the WindowManager.LocateForViewType: public static Func<Type. This means that you can customize them by simply replacing them with your own implementations. Here. One of the functions of LocateForModel is to inspect your ViewModel to see if it implements IViewAware. this creates your MainWindow. you don’t have to have codebehinds at all. thus causing conventional composition of views to occur.FullName. . by no means are you limited to these simple patterns. You can delete them if that makes you happy :) You should also know that ViewLocator.Other Things To Know Besides instantiation of your View. which calls it to determine how any dialog ViewModels should be rendered. See the section below on property binding conventions.Micro prefers ViewModel-First development. Customization The out-of-the-box convention is pretty simple and based on a number of patterns we’ve used and seen others use in the real world. However. if(!typeName. or it can be added by convention. create a new Func that calls the old and and assign the new Func to ViewLocator. You can use the View. three places where you can expect the view location conventions to be applied. your root ViewModel is passed to the locator in order to determine how your application’s shell should be rendered. In fact.Context attached property for contextual rendering). there are times when you may want to take a View-First approach. You’ll notice that all the methods discussed above are implemented as Funcs rather than actual methods. in WPF the bootstrapper delegates this to the WindowManager. Whenever you do ViewModel-First composition rendering by using the View. especially when working with WP7. then it passes your ViewModel’s type to LocateForModelType. This means that for Views created by the ViewLocator. If so.LocateForModelType is never called directly.Model attached property explicitly in your UI (optionally combining it with the View. ViewModel Resolution (View-First) Basics Though Caliburn. you will likely then need to resolve a ViewModel. simply store the existing Func in a variable. The third and final place that leverages these conventions is the View. In WPF.Model attached property on a UIElement. In the case where you start with a view.3 Framework Usage There are three places that the framework uses the ViewLocator.LocateForModel.EndsWith("View")) typeName += "View". This allows for a more SupervisingController style design. it follows the same patterns as the above framework services. this service is responsible for wiring up actions to the ApplicationBar’s Buttons and Menus.4 It also checks to see if your ViewModel implements IViewAware. No problem. using just the derived name. it creates the binding or the action on your behalf. It’s used by the FrameAdapter which insures that every time you navigate to a page. if that fits your scenario better. regardless of whether you use a ViewModel-First or a View-First approach. Customization Should you decide that you don’t like the behavior of the ViewModelBinder (more details below). minus all the namespace stuff. and if so. To do this. }. The final important thing the ViewModelBinder does is determine if it needs to create any conventional property bindings or actions. var key = viewModelName. only then will it call into LocateForViewType. if the View you are binding is a PhoneApplicationPage. Simply replace the Func with your own implementation. This method sets the Action. key). Instead we pull them by key. passes the View to your ViewModel. it is supplied with the correct ViewModel.LastIndexOf(". It’s called internally by ViewModelLocator.LocateForView. "ViewModel").LocateForViewType is actually never called directly by the framework. It could be easily adapted for use by the Silverlight Navigation Framework if desired. A final thing to note is that automatic InitializeComponent calls are not supported by view first. If the DataContext is null. return IoC. it doesn’t really require that much code to map one thing to another.GetInstance(null.Bind method is called. the ViewModelBinder. by its nature.Substring(viewModelName. ViewModelBinder Basics When we bind together your View and ViewModel.var viewModelName = typeName.Replace("View". with ViewModel location we change “View” to “ViewModel. Other Things To Know On the WP7 platform. Other Things To Know ViewModelLocator. When a match is found. it searches the UI for a list of element candidates for bindings/actions and compares that against the properties and methods of your ViewModel. Customization You may not be happy with the way that LocateForViewType retrieves your ViewModel from the IoC container by key. Because your ViewModels may be registered by an interface or a concrete class. LocateForView first checks your View instance’s DataContext to see if you’ve previous cached or custom created your ViewModel. Framework Usage The ViewModelLocator is only used by the WP7 version of the framework.” The other interesting difference is in how we get the instance of the ViewModel itself.") + 1).Target of the View to the ViewModel and correspondingly sets the DataContext to the same value. While with View location we change instances of “ViewModel” to “View”. As you can see. we don’t pull them from the container by type. It has several Funcs you can replace with . See the WP7 specific docs for more information on that. The final place that the ViewModelBinder is used is in the WP7 version of the framework. So.5 Basically this method does two things. the ViewModelBinder is uses to connect the ViewModel to the page. locates a view using the ViewLocator and then passes both of them along to the ViewModelBinder. it begins it’s second task: locating all elements in that scope that have names. This property takes a ViewModel and passes it along with the element on which the property is defined to the ViewModelBinder. Inside of the FrameAdapter. the ViewModelBinder “searches the UI for a list of element candidates for bindings/actions and compares that against the properties and methods of your ViewModel. In WPF. The elements that are returned by this function are then used by the ViewModelBinder to apply conventions. It can only search the visual tree. is the ability to turn off the binder’s convention features. when a page is navigated to. The search is careful to respect an “inner” scope boundary by not traversing inside of child user controls.Items. this is View-First. since you have already instantiated the View inline in your Xaml and are then just invoking the binding against a ViewModel.” The first step in understanding how this works is knowing how the framework determines which elements in your UI may be candidates for conventions.ApplyConventionsByDefault to false. Then. set ViewModelBinder. Customization You may not encounter issues related to the above limitations of element location.Header and HeaderedItemsControl. such as a Window. the View is injected inside the element on which the property is defined. This means it walks the tree until it finds a suitable root node. The first place is inside the implementation of the View. the ViewModelLocator is first used to obtain the ViewModel for that page. What this means is that things like ContextMenus. if you have conventions on by default. The second place that uses the ViewModelBinder is inside the implementation of the Bind. it also searches HeaderContentControl.your own implementations. Framework Usage The ViewModelBinder is used in three places inside of Caliburn.Model attached property. but need to turn them off on a view-by-view basis. Once it defines the “outer” borders of the scope. Other Things To Know There are a few limitations to what the GetNamedElementsInScope method can accomplish out-ofthe-box. UserControl or element without a parent (indicating that we are inside a DataTemplate). As mentioned above. I want to dig into the details of how it applies conventions. It does this by using a func on the static ExtensionMethods class called GetNamedElementsInScope.Content and ItemsControl.Header. But if you do. you can easily replace the default implementation with your own. First. Probably the most important aspect of customization though. you can set the View. BindActions and BindProperties. This attached property works both ways. In other words.Model attached property.Micro. such as Bind. Here’s an interesting technique you . That’s the ViewModel-First usage pattern. This property takes your ViewModel. ContentControl. Tooltips or anything else that isn’t in the visual tree or one of these special locations won’t get found when trying to apply conventions. it identifies a scope to search for elements in. If you want to enable it on a view-by-view basis. you just set this property to false. To do this. Element Location Basics Now that you understand the basic role of the ViewModelBinder and where it is used by the framework. After binding is complete.ApplyConventions attached property to true on your View. it is skipped. but FindName is case-sensitive.Length > 0) { message += "(". you can easily make the modification I just described by creating your new implementation in the same assembly as the views.Name. Other Things To Know The conventional actions are created by setting the Message.Contains(specialValue)) paramName = specialValue. To be on the safe side. because it is not guaranteed to succeed in Silverlight. we have to use our custom implementation which does a case-insensitive search.might choose to use: If the view is a UserControl or Window. We know that when Xaml files are compiled. I don’t provide this implementation out-of-the-box. a private field is created for everything with an x:Name. you could write a little bit of plumbing code that would allow the GetNamedElementsInScope Func to find the assembly-specific implementation which could actually perform the reflection.Attach and your action contains parameters.Name. However.Triggers on the element. The reason is due to the fact that Silverlight doesn’t allow you to get the value of a private field unless the calling code is the code that defines the field. if you have declared any triggers on the matched element. Furthermore. It would seem that we could just do a simple FindName.GetParameters(). Whenever you use Message. The check for pre-existing triggers is used to prevent the convention system from creating duplicate actions to what the developer may have explicitly declared in the markup. If a match is found. use some reflection to discover all private fields that inherit from FrameworkElement. an action is attached.Attach attached property on the element. instead of walking the tree for elements. It does this by using a bit of reflection to get the public methods of the ViewModel. . Action Matching Basics The next thing the ViewModelBinder does after locating the elements for convention bindings is inspect them for matches to methods on the ViewModel. You will have to fall back to the existing implementation for DataTemplate UI though. Let’s look at how that is built up: var message = method.6 Framework Usage I’ve already mentioned that element location occurs when the ViewModelBinder attempts to bind properties or methods by convention. As a result. and there aren’t any pre-existing Interaction.ToLower(). if(MessageBinder. the message parser has to find the elements that you are using as parameter inputs. Use this to your advantage. It then loops over them looking for a case-insensitive name match with an element. there is a second place that uses this functionality: the Parser. if you have a multi-assembly project. if all of your views are defined in a single assembly.SpecialValues. But. if(parameters. var specialValue = "$" + paramName. var parameters = method. foreach(var parameter in parameters) { var paramName = parameter. This ensures that the same semantics for binding are used in both places. BindActions is a Func and thus can be entirely replaced if desired.Name. 1). no event is declared. and the convention itself. method.message += paramName + ". Adding to or changing the ElementConventions via the ConventionManager will also effect how actions are put together. Because we don’t declare an event as part of the message.Remove(message. then we would get an EventTrigger with it’s Event set to Click.".Triggers collection of the element.Attach property is set. we move on to property binding. See the sections on ConventionManager and ElementConventions below for more information on that. we then get the ElementConventions from the ConventionManager so we can determine just how databinding on that element should occur. Once a match is found. FrameworkElement.Length . This Func is responsible for creating the binding on the element using all the contextual information provided. The neat thing is that we can have custom binding behaviors for each element if we want. element. It follows a similar process by looping through the named elements and looking for case-insensitive name matches on properties. The ElementConvention is used to create the Trigger and then the parser converts the action information into an ActionMessage. message). we make sure to append the “$” to it so that it will be recognized correctly by the Parser and later by the MessageBinder when the action is invoked. if the message was being attached to a Button. property info.SetAttach(foundControl.1. The ElementConvention defines an ApplyBinding Func that takes the view model type. As you can see. This string contains only the action part of the message. the Parser looks up the default Trigger for the type of element that the message is being attached to. property path. CM defines a basic implementation of ApplyBinding for most elements. Property Matching Basics Once action binding is complete. ElementConvention. When the Message. This information is configured through the ConventionManager with reasonable defaults out-of-the-box.Info("Added convention action for {0} as {1}. For example. } Log. message). element instance. which lives on the ConventionManager. the Parser immediately kicks in to convert the string message into some sort of TriggerBase with an associated ActionMessage. More on that below. message += ")". Message. we build a string representing the message. Framework Usage BindActions is used exclusively by the ViewModelBinder. path.". convention) => { . property. The two are connected together and then added to the Interaction. You can also see that it loops through the parameters of the method so that they are included in the action. } message = message. bool> SetBinding = (viewModelType. It’s called SetBinding and looks like this: public static Func<Type. Customization ViewModelBinder. If the parameter name is the same as a special parameter value. PropertyInfo. string. there are custom binding behaviors for ItemsControl and Selector. If none of these are set. Once the binding is fully constructed we add it to the element and return true indicating that the convention was applied. property). Next we check to see if there is already a binding set for that property.com/winfx/2006/xaml/presentation" xmlns:cal="clr-namespace:Caliburn. so we return false indicating that a binding has not been added. Assuming no binding exists. ApplyBindingMode(binding.Micro"> <ContentControl cal:View. we don’t want to overwrite it. Other Things To Know I mentioned above that “CM defines a basic implementation of ApplyBinding for most elements. We can match on deep property paths by convention as well. property). possibly . property).SetBinding(element.7 So. So. Hopefully that part makes sense.assembly=Caliburn.Model attached property. the ApplyBinding func also inspects the ItemTemplate. pass the item and the view to the ViewModelBinder (which in turn sets it’s own conventions. this method then basically delegates to other methods on the ConventionManager for the details of binding application. For WPF and Silverlight. binding).Micro. return true. property info and property path along to the ElementConvention’s ApplyBinding func. viewModelType. The developer is probably doing something special here. There’s another important aspect to property matching that I haven’t yet mentioned. we create the possibility of rich composition for ItemsControls. binding). bindableProperty. If there is. The first thing this method does is get the dependency property that should be bound by calling GetBindableProperty on the ElementConvention. the View.Model attached property allows us to invoke the ViewModel-First workflow: locate the view for the item. BindingOperations.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" /> </DataTemplate> Since this template creates a ContentControl with a View. In addition to binding the ItemsSource on an ItemsControl. ApplyValidation(binding. ApplyStringFormat(binding. it should add one conventionally. So. ApplyValueConverter(binding. ApplyUpdateSourceTrigger(bindableProperty. let’s say that you have a Customer property on your ViewModel that has a FirstName property you want to bind a Textbox to. DisplayMemberPath and ItemTemplateSelector (WPF) properties. element.microsoft. property). whatever the Item is. convention. then the framework knows that since you haven’t specified a renderer for the items. bindableProperty)) return false.” It also defines several custom implementations of the ApplyBinding Func for elements that are typically associated with specific usage patterns or composition. Here’s what it looks like: <DataTemplate xmlns="http://schemas.var bindableProperty = convention. Simply give the TextBox an x:Name of “Customer_FirstName” The ViewModelBinder will do all the work to make sure that that is a valid property and will pass the correct view model type. bindableProperty. if(HasBinding(element.GetBindableProperty(element). }. var binding = new Binding(path). we set the ItemTemplate to a default DataTemplate. we have one more that is important: ContentControl. we have a special ApplyBinding behavior for the TabControl. If the TabControl’s DisplayMemberPath is not set and the ViewModel implements IHaveDisplayName. If not. you haven’t specified a renderer for your model. we assume that you want to use the ViewModel-First workflow.com/winfx/2006/xaml/presentation"> <TextBlock Text="{Binding DisplayName. . For WPF.8 It takes on all the conventions of Selector (setting it’s ContentTemplate instead of ItemTemplate to the DefaultDataTemplate). I hope you will find that these special cases make sense when you think about them. but with an additional convention around the SelectedItem property. we enable rich composition. nearly every aspect of the ConventionManager or of a particular ElementConvention is customizable. Let’s say that your Selector is called Items. the BindProperties functionality is completely customizable by replacing the Func on the ViewModelBinder. Then. In all other cases ContentControl would be bound on the Content property. then we set it’s ItemTemplate to the DefaultHeaderTemplate. but we do supply a custom GetBindableProperty Func. you can change them… Customization As you might imagine. plus an additional convention for the tab header’s content. Mode=TwoWay}" /> </DataTemplate> So.Model property in the absence of a ContentTemplate. As always. However. So. you could just replace this Func with one that doesn’t do anything. the tab item’s name (ItemTemplate). Selectors have the same behavior as ItemsControls. we create a binding if we find one of these on the ViewModel. For example. if you like the idea of Action conventions but not Property conventions. Fortunately. we don’t supply a custom ApplyBinding Func. If we find one of these. for a named WPF TabControl we can conventionally bind in the list of tabs (ItemsSource). Then. if you don’t like them. we inspect the ContentTemplate and ContentTemplateSelector (WPF). it’s likely that you will want more fine grained control. For ContentControl. In this case “Items” becomes “Item” Then we call ConventionManager. That’s not bad for one line of Xaml like this: <TabControl x:Name="Items" /> In addition to the special cases listed above.Model attached property as the property to be bound. the pattern here is that we first call ConventionManager. We implement this by having the GetBindableProperty Func return the View. We follow the above conventions first by binding ItemsSource to Items and detecting whether or not we need to add a default DataTemplate.Singularize on the collection property’s name.DerivePotentialSelectionNames which prepends “Active” “Selected” and “Current” to “Item” to make the three candidates above. By selecting the View. In this case. we check to see if the SelectedItem property has been bound. More details about the ConventionManager are below. Therefore. which looks like this: <DataTemplate xmlns="http://schemas. If they are both null. we add the binding.microsoft.invoking more composition) and then take the view and inject it into the ContentControl. we look for three candidate properties on the ViewModel that could be bound to SelectedItem: ActiveItem. the content for each tab (ContentTemplate) and keep the selected tab synchronized with the model (SelectedItem). SelectedItem and CurrentItem. when we go to determine which property to bind to. convention) => { ConventionManager . Uses Singularize.SelectedItemProperty. viewModelType. property. viewModelType).Applies the appropriate binding mode to the binding. Uses the following Funcs internally: o HasBinding . Used by ApplyValueConverter. }. The default implementation is really basic and just strips the trailing ‘s’. returns a list of possible property names representing the selection. ConventionManager . Here’s an example of how you would set up an advanced convention for the WP7 Pivot control which would make it work like the WPF TabControl: ConventionManager. o ApplyBindingMode . element. It is the gateway to fine-tuning the majority of the convention behavior in the framework.ApplyBinding = (viewModelType.ApplyHeaderTemplate(element.Determines whether a particular dependency property already has a binding on the provided element. path. IncludeStaticProperties . DefaultItemTemplate – Used when an ItemsControl or ContentControl needs a DataTemplate.ItemsSourceProperty. Changing this will change how all conventional bindings are applied. Funcs    Singularize – Turns a word from its plural form to its singular form. ConventionManager . "SelectedItem". False by default. property. Pivot. .ApplyBinding(viewModelType. DefaultHeaderTemplate – Used by ApplyHeaderTemplate when the TabControl needs a header template. SetBinding – The default implementation of ApplyBinding used by ElementConventions (more info below). path. DerivePotentialSelectionNames – Given a base collection name.GetElementConvention(typeof(ItemsControl)) . convention). If a binding already exists.HeaderTemplateProperty. What follows is a list of the replaceable Funcs and Properties which you can use to customize the conventions of the framework: Properties     BooleanToVisibilityConverter – The default IValueConverter used for converting Boolean to Visibility and back. Pivot.AddElementConvention<Pivot>(Pivot. "SelectionChanged"). path).ConfigureSelectedItem(element. Pretty cool? Framework Usage BindProperties is used exclusively by the ViewModelBinder. SetBinding is aborted. Most commonly this will be in adding the Silverlight toolkit controls or the WP7 toolkit controls.Indicates whether or not static properties should be included during convention name matching.One of the common ways you will configure conventions is by adding new conventions to the system. ConventionManager If you’ve read this far. element. you know that the ConventionManager is leveraged heavily by the Action and Property binding mechanisms. Determines whether a custom update source trigger should be applied to the binding.o o o o ApplyValidation .AddElementConvention. ApplyUpdateSourceTrigger .Attach="MyMethod(something)" /> When the Button’s ActionMessage is created. It only checks for BooleanToVisibility conversion by default. searches the type hierarchy for a match. the ElementConvention will be looked up and the ParameterProperty will be used. the Button’s ElementConvention will be looked up . when the ActionMessage is created for the Button. always sets to UpdateSourceTrigger=PropertyChanged. if a parameter that refers to an element is specified. For WPF.Attach is used to declare an Action. Funcs   GetBindableProperty – Gets the property for the element which should be used in convention binding. ApplySilverlightTriggers – For TextBox and PasswordBox. Methods      AddElementConvention – Adds or replaces an ElementConvention. ParameterProperty – When using Message. which is a TextBox. For example in the Xaml above. if we have this markup: <TextBox x:Name="something" /> <Button cal:Message. By default. For Silverlight. and the specific event is not specified.” Therefore. ElementConvention ElementConventions can be added or replaced through ConventionManager.Determines whether a custom string format is needed and applies it to the binding. ConfigureSelectedItem – Configures the SelectedItem convention on an element. it’s important to know what these conventions are and how they are used throughout the framework. uses the format "{0:MM/dd/yyyy}". calls ApplySilverlightTriggers. For example.Text. But.Determines whether a value converter is is needed and applies one to the binding. We get the ElementConvention for TextBox which has its ParameterProperty set to “Text. we look up “something”. ApplyStringFormat . the ElementConvention will be looked up and the CreateTrigger Func will be called to create the Interaction.Trigger.Attach to declare an action. if binding to a DateTime. CreateTrigger – When Message.Determines whether or not and what type of validation to enable on the binding. we create the parameter for MyMethod from something. GetElementConvention – Gets the convention for a particular element type. Here are the Properties and Funcs of the ElementConvention class with brief explanations: Properties   ElementType – The type of element to which the convention applies. ApplyHeaderTemplate – Applies the header template convention to an element. wires the appropriate events to binding updates in order to simulate WPF’s UpdateSourceTrigger=PropertyChanged. but the property of that element is not. If not found. At the very bottom of this article is a code listing showing how all the elements are configured out-of-the-box. ApplyValueConverter . "SelectedItem". "DataContext". "Loaded"). "Loaded"). "Password". "SelectedItemChanged").ApplyBinding = (viewModelType. By default this just passes through to ConventionManager. "Source". convention) => { if (!SetBinding(viewModelType. }. AddElementConvention<ItemsControl>(ItemsControl.ItemsSourceProperty. "ValueChanged").SelectedItemProperty. "DataContext". "Loaded") . AddElementConvention<MenuItem>(MenuItem. if(tabControl. "ItemsSource". Out-Of-The-Box Element Conventions Following is the full code-listing showing how the built-in controls have their ElementConventions configured out-of-the-box: #if SILVERLIGHT AddElementConvention<HyperlinkButton>(HyperlinkButton. path. "Click"). #endif AddElementConvention<UserControl>(UserControl.ContentProperty.GetGenericArguments(). property. "Expanded"). "Loaded"). path. property. "Click"). AddElementConvention<Window>(Window. ApplyBinding – As described above.SelectedItemProperty. AddElementConvention<TabControl>(TabControl. ConfigureSelectedItem(element. "Click").ItemTemplateProperty. "IsExpanded".PropertyType. convention) => { if(!SetBinding(viewModelType. property.DisplayMemberPath)) ApplyHeaderTemplate(tabControl. AddElementConvention<Menu>(Menu.ItemsSourceProperty.SourceProperty.IsValueType && !typeof(string). element. "Text". var tabControl = (TabControl)element. element. "Content". AddElementConvention<Image>(Image. "PasswordChanged"). "DataContext". property. AddElementConvention<TextBox>(TextBox. convention)) . AddElementConvention<Hyperlink>(Hyperlink.IsCheckedProperty.VisibilityProperty.ContentTemplateSelector == null && property. AddElementConvention<PasswordBox>(PasswordBox. "Text". path.TextProperty. AddElementConvention<TreeView>(TreeView.DataContextProperty. "Click").ContentTemplate = DefaultItemTemplate. But certain elements (see above…or below) customize this in order to enable more powerful composition scenarios. #else AddElementConvention<PasswordBox>(PasswordBox. "DataContext". AddElementConvention<Selector>(Selector. "DataContext". "DataContext". "DataContext". In this case. and its CreateTrigger Func will be called. "Loaded"). AddElementConvention<RichTextBox>(RichTextBox.ContentProperty.IsExpandedProperty. "TextChanged"). "SelectedItem". "Loaded"). AddElementConvention<TabItem>(TabItem. convention)) return. "Value".PropertyType. element. convention)) return.ApplyBinding = (viewModelType. "TextChanged").ContentProperty.DataContextProperty. Selector.ItemsSourceProperty.DataContextProperty. TabControl.ContentTemplate == null && tabControl. viewModelType. property.TextProperty. "Loaded"). if(!itemType.ItemsSourceProperty. "DataContext". convention) => { if (!SetBinding(viewModelType. "DataContext". "SelectionChanged") . AddElementConvention<Expander>(Expander. AddElementConvention<Slider>(Slider.ContentProperty.ApplyBinding = (viewModelType.ValueProperty. the element we are binding has its ElementConvention looked up and it’s ApplyBinding func is called. element.IsNullOrEmpty(tabControl.ItemsSourceProperty."DataContext". "DataContext". "PasswordChanged"). AddElementConvention<TextBlock>(TextBlock.IsAssignableFrom(itemType)) tabControl. Selector. AddElementConvention<StatusBar>(StatusBar. "DataContextChanged").PasswordProperty. "SelectionChanged") . "DataContextChanged"). } ConfigureSelectedItem(element. path. "Click").ItemsSourceProperty. path. "DataContextChanged"). AddElementConvention<ToggleButton>(ToggleButton.ItemsSourceProperty.SetBinding. path). viewModelType). ConfigureItemsControl((ItemsControl)element. }.IsGenericType) { var itemType = property. AddElementConvention<Label>(Label. "DataContext". element. path). "DataContext". when conventional databinding occurs. element. property). path. AddElementConvention<ToolBarTray>(ToolBarTray. "Click"). the ElementConvention returns an EventTrigger configured to use the Click event.VisibilityProperty. AddElementConvention<ToolBar>(ToolBar.ItemsSourceProperty. property. "IsChecked". "DataContext".DataContextProperty.viewModelType.First(). if(string. AddElementConvention<ButtonBase>(ButtonBase. So. AddElementConvention<ContentControl>(ContentControl. it still lives on the ExtensionMethods class.VisibilityProperty. Recall that in Caliburn. 2. You can also add additional assemblies to this collection at any time during application execution. "Loaded").ContentProperty. DataContext and Action.ContentTemplate == null && element. To change this you can set DataContext explicitly to a different value or more commonly. It inspects types to see if they are UserControl or Window/ChildWindow. So. 3. "DataContext".Micro DataContext has the purpose of specifying to what instance databinding expressions are resolved and Action.Content is DependencyObject) ? View. We don’t have this convention in Silverlight because A. AddElementConvention<Shape>(Shape. The bootstrapper is responsible for making sure that your application’s assembly is added to the AssemblySource. "DataContext". 6. Silverlight’s TabControl is broken for databinding and has been for two versions of the framework without a fix. However. I typically make views UserControls when I can because that makes them a bit more reusable. This pattern is borrowed from Javascript and I felt it generally made extensibility simpler for the most common use cases.ContentProperty. whenever the Action. AddElementConvention<FrameworkElement>(FrameworkElement.ContentProperty. The WindowManager uses the ViewLocator under the covers.Target are allowed to vary independently of one another. you can choose to create your dialog views either as a UserControl or Window/ChildWindow in this particular scenario and the framework will take care of the rest.com/workitem/3604 It was reported about a year and a half ago and is not yet fixed. it became apparent that developers wanted to customize the implementation. you can use the Action. I made it into a Func. For lack of a better place to put it. it automatically creates a parent Window/ChildWindow. "Loaded").ModelProperty : ContentControl. 4. }. So.Content is DependencyObject) ? View. property). Footnotes 1. Thus. #endif }. if you are dynamically downloading modules that contain more views. 5. I assume this would work.Target is set. By default.ModelProperty : ContentControl. "MouseLeftButtonUp"). I haven’t actually tried it though. the DataContext is set to the same value though. We assume that you want to use the default ToString rendering that the platform provides. but you can add any additional assemblies by overriding the Bootstrapper’s SelectAssemblies method and returning the full list.ContentTemplate == null && !(element. 7.ContentTemplateSelector == null && !(element. #else return element. If they are not.Target specifies the instance that handles an Action. TabControl is not part of the core framework and B. . #if SILVERLIGHT return element.Target without affecting the DataContext. 8.codeplex.GetBindableProperty = delegate(DependencyObject foundControl) { var element = (ContentControl)foundControl. Please vote this issue up http://silverlight.VisibilityProperty. If your item is a ValueType or a String we don’t generate a default DataTemplate. "DataContext". ConfigureItemsControl((ItemsControl)element.return. GetNamedElementsInScope used to be an extension method. Well. you can handle that scenario pretty easily.TargetWithoutContext attached property to set only the Action. Should you find yourself in a situation where you are building a small application or something for WP7 where you cannot or do not want to use a full featured IoC container. public void RegisterInstance(Type service. the following code should provide you with a solution. key. Type implementation) { object singleton = null. Implementation using using using using using System. Constructor injection of IEnumerable<TService> Overridable instance activation. Type implementation) { RegisterHandler(service. string key) { var entry = GetEntry(service. () => singleton ?? (singleton = BuildInstance(implementation))). } public void RegisterSingleton(Type service.Collections. . System.Collections. public class SimpleContainer { readonly List<ContainerEntry> entries = new List<ContainerEntry>(). if(entry != null) return entry. RegisterHandler(service. Features           Singleton Registration PerRequest (Transient) Registration Instance Registration Registration of Arbitrary Handlers Get a single instance by Type and/or key. Automatic greedy constructor injection.Linq. object implementation) { RegisterHandler(service. System. key.Single()(). () => implementation). key. Func<object> handler) { GetOrCreateEntry(service. () => BuildInstance(implementation)). } public object GetInstance(Type service. string key. such a component is not included out-of-the-box with CM. System. string key. string key. Automatic Func<T> factory creation.Recipes SimpleContainer Caliburn. } public void RegisterPerRequest(Type service. key). key).Add(handler).Micro is best used with an Inversion of Control (IoC) Container. System. Get all instances of a particular type. } public void RegisterHandler(Type service.Reflection. Because many developers have a preference for a particular container.Generic. string key. PropertyType).ToList(). } ContainerEntry GetOrCreateEntry(Type service. } return null.Select(x => x()) : new object[0].CreateInstance(type.CreateInstance(type). entries. return ActivateInstance(type. string key) { var entry = GetEntry(service.GetMethod("Create").SetValue(instances[i]. } protected virtual object ActivateInstance(Type type.Length.CanWrite && property. var instances = GetAllInstances(listType). i++) { array.IsInterface select property. if(injection.IsAssignableFrom(service)) { var typeToCreate = service.Count).if(typeof(Delegate).GetType().Where(x => x. var factoryFactoryHost = Activator. var factoryFactoryType = typeof(FactoryFactory<>). } return entry. }).Key == key). new object[] { this }). for(var i = 0. null).FirstOrDefault() : entries. } protected object BuildInstance(Type type) { var args = DetermineConstructorArgs(type).CreateInstance(listType.CanRead && property.FirstOrDefault().Apply(x =>{ var injection = GetAllInstances(x. } return array. } ContainerEntry GetEntry(Type service.GetGenericArguments()[0]. if (entry == null) { entry = new ContainerEntry { Service = service. var array = Array. args) : Activator. var factoryFactoryMethod = factoryFactoryType.GetGenericArguments()[0].GetProperties() where property.SetValue(instance. key). } public IEnumerable<object> GetAllInstances(Type service) { var entry = GetEntry(service. i). string key) { return service == null ? entries. i < array.Any()) x.Length > 0 ? Activator. injection. args).MakeGenericType(typeToCreate).CreateInstance(factoryFactoryType). object[] args) { return args.Add(entry).Service == service && x. return entry != null ? entry. } else if(typeof(IEnumerable).Invoke(factoryFactoryHost.First().Key == key). } object[] DetermineConstructorArgs(Type implementation) .IsAssignableFrom(service)) { var listType = service.Where(x => x. } public void BuildUp(object instance) { var injectables = from property in instance.PropertyType. instances. return factoryFactoryMethod. null). Key = key }. injectables. GetParameters(). } class FactoryFactory<T> { public Func<T> Create(SimpleContainer container) { return () => (T)container.GetConstructors() orderby c. if (constructor != null) args.AddRange(constructor. var constructor = SelectEligibleConstructor(implementation). null))).Length descending select c).FirstOrDefault().GetInstance(typeof(T). } static ConstructorInfo SelectEligibleConstructor(Type type) { return (from c in type.Select(info => GetInstance(info.ToArray(). } class ContainerEntry : List<Func<object>> { public string Key.GetParameters(). return args. public Type Service. null). } } } .ParameterType.{ var args = new List<object>(). Value.Select(part => ReflectionModelServices. DeploymentCatalog> Catalogs = new Dictionary<string.DownloadCompleted += (s.TryGetValue(uri. out catalog)) Completed(this.ComponentModel.GetPartType(part). new ResultCompletionEventArgs { Error = e.Collections. Catalog.Error. } public LoadCatalog(string relativeUri) { uri = relativeUri. In order for it to work properly.Contains(assembly)) .LoadCatalog Result Contributed by janoveh The following is a simple IResult implementation for using MEF's DeploymentCatalog to dynamically download and plug in xaps. DeploymentCatalog>().Add(catalog).DownloadAsync().Error == null) { Catalogs[uri] = catalog. System. e) =>{ if(e.Hosting.Where(assembly => !AssemblySource. catalog. [Import] public AggregateCatalog Catalog { get. set. Implementation using using using using using using System. catalog.Generic.Parts .ComponentModel. WasCancelled = false }).Instance.Catalogs. readonly string uri.Composition. System. if(Catalogs.Composition.Apply(x => AssemblySource. public class LoadCatalog : IResult { static readonly Dictionary<string.Add(x)). you must make sure to add the AggregateCatalog as an exported value in the container. new ResultCompletionEventArgs()). } public void Execute(ActionExecutionContext context) { DeploymentCatalog catalog.Composition.Instance. .Linq. System.ComponentModel. System.Assembly) . else { catalog = new DeploymentCatalog(uri). catalog. }.ReflectionModel. } Completed(this. System. yield return new LoadCatalog("TestCatalog3. } Usage public IEnumerable<IResult> LoadCatalogs() { yield return new LoadCatalog("TestCatalog1. yield return new LoadCatalog("TestCatalog2. .xap").} } public event EventHandler<ResultCompletionEventArgs> Completed = delegate { }.xap").xap").xap"). } Much thanks to codeplex user janoveh on whose work this recipe was based. yield return new LoadCatalog("TestCatalog4. } } IExecutionWrapper Most filter frameworks are built around AOP concepts and work through the interception of action execution. Download the Sample Solution Design Filters falls into two main categories. ActionMessage. They allow the developer to share the implementation of cross-cutting concerns between different View Models by moving it into infrastructure components. Doing this allows you to add operations before and after the action execution itself or something more complex like dispatching the action in another thread.Configure().. The common IFilter interface is little more than a marker and just defines a Priority property used to guide the filter application order: public interface IFilter { int Priority { get. } . a set of action decorations aimed to provide additional behaviors to a particular action.InvokeAction = InvokeAction. FilterFramework. Filters bring two major advantages:   They help to keep View Model free from noisy code that is unrelated to its main purpose. replace the core invocation method of ActionMessage: //in bootstrapper code . This greatly simplifies unit testing.. public static class FilterFramework { public static void Configure() { . } To enable filter hooking.. public static void InvokeAction(ActionExecutionContext context) { . depending on the mechanism used to interact with the target action: IExecutionWrapper and IContextAware.. so a filter willing to intercept an action can simply wrap the original execution into a “wrapping” IResult: public interface IExecutionWrapper : IFilter { IResult Wrap(IResult inner). ..Micro is filters..Action Filters Contributed by Marco Amendola One of the features missing in Caliburn... The coroutine infrastructure already has this capability. e.GetParameters()).. }.OfType<IExecutionWrapper>().GetAttributes<IFilter>(true)) . context. It does this by conforming all of them to a common IResult interface. the other filter category. } .Method. context. operates when an action is not executing. action execution should throw! pipeline.GetType().Message). providing preconditions for execution (the .".. var result = Coroutine. } } Note that the GetFiltersFor method is replaceable to allow for another filter lookup strategy (for example.Method. IContextAware.DetermineParameters(context. (current. e) => { Execute. } Every action is actually executed within an ExecuteActionResult (code omitted here) that deals with simple actions as well as coroutines.Completed += (o. //if pipeline has error. IEnumerable<IFilter>> GetFiltersFor = context => { //TODO: apply caching? return context.OrderBy(x => x.Aggregate(inner. This “core” IResult is afterwards wrapped over and over by each filter attached to the action and finally executed. var pipeline = result.GetAttributes<IFilter>(true) . wrapper) => wrapper. pipeline.Error != null) { throw new Exception( string.Priority).GetFiltersFor(context). var wrappers = FilterManager.Format("An error occurred while executing {0}.GetEnu merator()). IContextAware While IExecutionWrapper-s do their work during the action execution.CreateParentEnumerator(ExecuteActionWithParameters(values). based on convention or external configuration instead of attributes).var values = MessageBinder.Wrap(current)).Target.OnUIThread(() => { if(e.Union(context. IEnumerable<IExecutionWrapper> wrappers) { return wrappers.WrapWith(wrappers).Error ). }.Execute(context). public static IResult WrapWith(this IResult inner. Let’s have a look at FilterManager: public static class FilterManager { public static Func<ActionExecutionContext. } }). } Filters implementing this interface are given a chance. the framework hooks into ActionMessage.Wrap(IResult inner) { this.related predicate is held by ActionExecutionContext) or observing the ViewModel to force an update of the action availability: public interface IContextAware : IFilter.Execute(ActionExecutionContext context) { if(!CanExecute(context)) { CompletedEvent(this. use the base class for IExecutionWrapper which includes all the boilerplate code and provides the standard customization points: public abstract class ExecutionWrapperBase : Attribute. event EventHandler<ResultCompletionEventArgs> CompletedEvent = delegate { }.Apply(x => x. return.inner = inner.InvokeAction = InvokeAction. } } Implementing Filters To simplify filters construction. context.Message. e) => { contextAwareFilters.ToArray().PrepareContext = context => { oldPrepareContext(context). } try { EventHandler<ResultCompletionEventArgs> onCompletion = null. } void IResult. set. IResult { IResult inner. }. to hook the execution context.PrepareContext: public static class FilterFramework { public static void Configure() { var oldPrepareContext = ActionMessage. public int Priority { get. } public static void PrepareContext(ActionExecutionContext context) { var contextAwareFilters = FilterManager.Apply(x => x. To achieve this. } IResult IExecutionWrapper. }. PrepareContext(context). IDisposable { void MakeAwareOf(ActionExecutionContext context). contextAwareFilters. return this. ActionMessage.OfType<IContextAware>() .Detaching += (o. during ActionMessage initialization.MakeAwareOf(context)).GetFiltersFor(context) . IExecutionWrapper.PrepareContext. new ResultCompletionEventArgs { WasCancelled = true }). .Dispose()). ActionMessage. ActionExecutionContext context) { ThreadPool. } catch(Exception ex) { FinalizeExecution(context.QueueUserWorkItem(state => { inner.Error).Completed -= onCompletion. false.Execute(context). CompletedEvent(this. bool wasCancelled. Exception ex) { return false.onCompletion = (o. BeforeExecute(context).WasCancelled. context). } } Sample Filters and Usage /// <summary> /// Provides asynchronous execution of the action in a background thread /// </summary> public class AsyncAttribute : ExecutionWrapperBase { protected override void Execute(IResult inner. Execute(inner. Error = ex }). } } protected virtual bool CanExecute(ActionExecutionContext context) { return true. ActionExecutionContext context) { inner. new ResultCompletionEventArgs { WasCancelled = wasCancelled. }. e. AfterExecute(context).Completed { add { CompletedEvent += value. FinalizeExecution(context. } protected virtual bool HandleException(ActionExecutionContext context. inner. e) => { inner. } protected virtual void BeforeExecute(ActionExecutionContext context) {} protected virtual void AfterExecute(ActionExecutionContext context) {} protected virtual void Execute(IResult inner. }). Exception ex) { if(ex != null && HandleException(context. } . ex).Execute(context). } remove { CompletedEvent -= value. } void FinalizeExecution(ActionExecutionContext context. } } event EventHandler<ResultCompletionEventArgs> IResult.Completed += onCompletion. e. ex)) ex = null. } } } //usage: //[Rescue] //public void ThrowingAction() //{ // throw new NotImplementedException(). } public string MethodName { get. // return true.ToString()).} //usage: //[Async] //public void MyAction() { .GetType(). } protected override bool HandleException(ActionExecutionContext context.. if (result is bool) return (bool)result.Show(ex. Exception ex) { var method = context. //} /// <summary> /// Sets "IsBusy" property to true (on models implementing ICanBeBusy) during the execution /// </summary> public class SetBusyAttribute : ExecutionWrapperBase { protected override void BeforeExecute(ActionExecutionContext context) { .GetMethod(MethodName. else return true.. new[] { typeof(Exception) }). if (method == null) return false. //} //public bool Rescue(Exception ex) //{ // MessageBox.Target.Target. private set. try { var result = method. } catch { return false. new object[] { ex }).Invoke(context. } /// <summary> /// Allows to specify a "rescue" method to handle exception occurred during execution /// </summary> public class RescueAttribute : ExecutionWrapperBase { public RescueAttribute() : this("Rescue") { } public RescueAttribute(string methodName) { MethodName = methodName. PropertyName)) { Execute. } protected override bool HandleException(ActionExecutionContext context. } void inpc_PropertyChanged(object sender. Exception ex) { SetBusy(context. true). INotifyPropertyChanged _inpc. false). } protected override void AfterExecute(ActionExecutionContext context) { SetBusy(context. .Target as INotifyPropertyChanged..OnUIThread(() => { _context. bool isBusy) { if (model != null) model.. IContextAware { ActionExecutionContext _context. } /// <summary> /// Updates the availability of the action (thus updating the UI) /// </summary> public class DependenciesAttribute : Attribute.UpdateAvailability(). } public void MakeAwareOf(ActionExecutionContext context) { _context = context. _inpc = null. public DependenciesAttribute(params string[] propertyNames) { PropertyNames = propertyNames ?? new string[] { }.Target as ICanBeBusy. return false. set.PropertyChanged += inpc_PropertyChanged.PropertyChanged -= inpc_PropertyChanged.Contains(e. thus allowing busy state representation //public void VeryLongAction() { . } public int Priority { get. } } //usage: //[SetBusy] //[Async] //prevents UI freezing. _inpc = context.Message. } private void SetBusy(ICanBeBusy model. PropertyChangedEventArgs e) { if (PropertyNames.SetBusy(context.Target as ICanBeBusy. } public void Dispose() { if (_inpc != null) _inpc.IsBusy = isBusy. private set. if (_inpc != null) _inpc.Target as ICanBeBusy. false). } public string[] PropertyNames { get. Target. IContextAware { public PreviewAttribute(string methodName) { MethodName = methodName. } //public bool CanDoAction() { return MyProperty > 0 && MyOtherProperty < 1.GetMethod("get_" + MethodName). MessageBinder. } public void MakeAwareOf(ActionExecutionContext context) { var targetType = context. if (guard == null) return. var guard = targetType. private set.}). } } } //usage: //[Dependencies("MyProperty"... } public void Dispose() { } } //usage: //[Preview("IsMyActionAvailable")] //public void MyAction(int value) { .. if (guard== null) guard = targetType.DetermineParameters(context..CanExecute = () => { if (oldCanExecute != null && !oldCanExecute()) return false.GetParameters()) ). set. context. } //public bool IsMyActionAvailable(int value) { . guard. }..Invoke( context. var oldCanExecute = context. "MyOtherProperty")] //public void DoAction() { .GetMethod(MethodName).. version 15 Comments ROBOlav Dec 22 2010 at 1:50 PM . } Download the Sample Solution Last edited Nov 15 2010 at 10:33 PM by EisenbergEffect. } public string MethodName { get.Target. } public int Priority { get. return (bool)guard.CanExecute.GetType(). } /// <summary> /// Allows to specify a guard method or property with an arbitrary name /// </summary> public class PreviewAttribute : Attribute. However. lately I've started using the IWindowManager. Removing the actions from my XAML-file preveted this.Thank you for this guide! I've used this with the Async-attribute. but then I don't get much work done :) Have any of you experienced the same.Show() -function to open Screens as separate windows. and it worked great. Doing this caused a run time NullReferenceException in the newly added FilterManager. have a quick fix? . and if so. Uri Navigation in Silverlight Is an Internet app really an Internet app if you can’t navigate using a URI? I don’t think so. you get the user hitting the back button and instead of going to the prior page in your app. I show how Caliburn can be used along with MEF Navigation to enable URI navigation which brings deep linking to your application. it has a property called ContentLoader. The Frame is integrated into the browser and can work with the browser’s navigation buttons (you can also set it to integrate with buttons in the app that work like the browser buttons.Windows. The default value of ContentLoader property is an instance of the class PageResourceContentLoader (in System. What follows is an implementation of Caliburn. as the name implies. he creates an example of a TypenameContentLoader which.Micro is a powerful MVVM framework.Controls. In this recipe.xaml. but passing a type name isn’t really . but what we would really like is a way to control the Frame so we can properly conduct the application even if it is being driven by a URI. but even a LOB app should allow you to use the backwards and forward buttons of the browser. The use of this interface was first covered by David Poll in Opening Up SL 4 Navigation .com/#/Page1 into a specific page. If it doesn’t support it. Briefly. The Silverlight Navigation Application template in Visual Studio does do just that.Micro for use with a Silverlight Navigation application. say in an Out-Of-The-Browser app).ContentLoader The Frame as mentioned above is not actually loading the content.Frame) in MainPage. in doing so it forces “View-first” architecture in your application because it must be able to translate http://example. So how can we enable a view-first architecture? To get there. In this blog.ContentLoader. This was a step in the right direction. Ecommerce since definitely needs this feature. the user gets thrown out of your app to the prior page in the browsers journal. INavigationContentLoader The release of Silverlight 4 brought us the INavigationContentLoader. The Navigation Frame Class In order to navigate. Navigation with the Frame works. This brings us to the Frame. Caliburn allows you do use Convention Binding which automatically binds your ViewModelto your view based on naming conventions.Windows. the SL Navigation template uses a control called a Frame (System. Backwards and Forward navigation is expected navigation for websites.Silverlight Navigation Contributed by Greg Gum Intro Caliburn. load the page based on a class name that is passed in through the URI. The PageResourceContentLoader is based on INavigationContentLoader and it is through this interface we can get back to a view-first implementation. This eliminates a great deal of hand coding that is normally required to wire up your VM and view using commands.Navigation). we first need to cover a view classes that are used in Silverlight URI navigation and then see how we can customize them to fit our own needs. Frame. However. And we want to use Caliburn.Micro. and can bring loading on demand to the table as well.Micro for its convention binding.Micro is doing the binding for you. Go to the MainPage and add an xmlns to MEFNavigation. 4. this class has a method called NavigateToPage that takes the target URI.what we want. Introducing the MEFContentLoader The MEF content loader does all that I just described. Start with the Silverlight Navigation Application template. Honestly. as well as MEFNavigation. The MEFContentLoader is in the sample application below. 3.ContentLoader> This is going to replace the default loader with our own MEF Loader. it really doesn’t get any easier than this. our actual view. Go to the Home Page. again with no further configuration. Add the following attribute to the Home class: [ExportPage("/Home")] public partial class Home : Page { //implementation elided } This attribute specifies to MEF that this class is a View. You could use other base types.ContentLoader> <mef:MEFContentLoader /> </navigation:Frame. with a base type of Screen (from Caliburn. But Screen adds a lot of benefits so is what I have used here. . just those two decorations. Wire up your other pages and those will work just like the above. Note that the base is of type Screen. And Caliburn. In addition.Micro). Add a reference to MEFNavigation. Remove the default he content loader and the URI mapper. Create a ViewModel. And that’s it! You don’t need to write any MEF code whatsoever. Let’s jump right into what you need to do to implement this class in your own application. like this: [ExportViewModel("Home")] public class HomePageViewModel : Screen { ///implementation elided } This is going to tell MEF that this is the ViewModel for the Home screen. gets the View using more MEF. gets the ViewModel using MEF. and that the tag for it is “Home” 5. Just fire up your project and it will go right to the home page. let’s make it MEF driven since it’s easily available in Silverlight 4. 1. What we really want is to be able to pass in a token such as “Home” to create our ViewModel and along with that. and then binds them together with Caliburn. Replace it with this xaml: <navigation:Frame.Micro. but you would have to update the MEFLoader. 2. Add a reference to Caliburn. In a nutshell. This reduces the load time of the initial xap. I modified this to pull out both the ViewModel and the Page. it’s loading on demand. Note that this page is in its own project and the Silverlight App does not reference this project. it looks for any other xap files in the website.I have not included a bootstrapper in this sample to keep it simple. see Greg's blog at http://SilverlightDev. Wrap Up There is still more to include in the MEFLoader to get it up to production quality:     Base the MEFContentLoader on the Caliburn. it will find the other zap in the ClientBin and pull it out of there. you know the binding is working. It is based on the samples which Glenn Block used in his Mix 10 MEF Demo. For more info.Micro samples. Support the default Page Navigation behavior as a fall-back of the ViewModel navigation fails. The page will tell you which page you are on. The MEFNavigation Project This project includes the classes that enable URI Navigation. But you don’t need to modify these to try them out in your own projects. These classes are worthy of a blog post in themselves.Micro Conductor and include lifecycle events. so I am going to hold off covering those classes. The Sample App The sample is just three pages. In the demo. Type in the textbox. So how is this page showing up? This is more MEF magic. Loading on Demand The sample application includes a third page called Reports. If you get a MessageBox. In other words. as the MEF initialization is occurring in the MEFContentLoader.net Download the Sample . Include an Error Page for broken links. A bootstrapper is not required to make the above work. but this could easily be added and would be the same as in other Caliburn. When the MEF loader fails to find the page in the local zap. Since there IS a reference to the Reports project in the Website. he used MEF to pull out the Page using MEF. created just like in the steps above. Support Deep Linking (to a specific productID for example). then click the button. as well as enabling the concept of plug-ins to your application. Just run the app. But the above is a step forward and something I think is going to become very useful to Uri based navigation in Silverlight.
Copyright © 2024 DOKUMEN.SITE Inc.