Quantcast
Channel: Caliburn.Micro: Xaml Made Easy
Viewing all 1760 articles
Browse latest View live

Source code checked in, #2033cfd61e8b853b9d2009a9abbb2a68e9e45666


Updated Wiki: Introduction

$
0
0

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 ~75k assembly (~35k 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 short period 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 Action 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 preceded 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– 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. We even support polymorphic subscriptions.
  • 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 its 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 View-Model by 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.
  • ViewModelLocator - Though Caliburn.Micro favors the ViewModel-First approach, we also support View-First by providing a ViewModelLocator with the same mapping semantics as the ViewLocator.
  • WindowManager– This service provides a View-Model-centric way of displaying Windows (ChildWindow in SL, Window in WPF, a custom native-styled host in WP7). 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 Bootstrapper 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 its 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 its 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, and looking forward to seeing your Pull requests.

Updated Wiki: Cheat Sheet

$
0
0

Cheat Sheet

This serves as a quick guide to the most frequently used conventions and features in the Caliburn.Micro project.

Wiring Events

This is automatically wiring events on controls to call methods on the ViewModel.

Convention


<Buttonx:Name="Save">

This will cause the Click event of the Button to call "Save" method on the ViewModel.

Short Syntax


<Buttoncal:Message.Attach="Save">

This will again cause the "Click" event of the Button to call "Save" method on the ViewModel.

Different events can be used like this:

<Buttoncal:Message.Attach="[Event MouseEnter] = [Action Save]">

Different parameters can be passed to the method like this:

<Buttoncal:Message.Attach="[Event MouseEnter] = [Action Save($this)]">
  • $eventArgs– Passes the EventArgs or input parameter to your Action. Note: This will be null for guard methods since the trigger hasn’t actually occurred.
  • $dataContext– Passes the DataContext of the element that the ActionMessage is attached to. 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.
  • $source– The actual FrameworkElement that triggered the ActionMessage to be sent.
  • $view - The view (usually a UserControl or Window) that is bound to the ViewModel.
  • $executionContext - The action's execution context, which contains all the above information and more. This is useful in advanced scenarios.
  • $this - The actual UI element to which the action is attached. In this case, the element itself won't be passed as a parameter, but rather its default property.

Long Syntax


<UserControlx:Class="Caliburn.Micro.CheatSheet.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"xmlns:cal="http://www.caliburnproject.org"><StackPanel><TextBoxx:Name="Name"/><ButtonContent="Save"><i:Interaction.Triggers><i:EventTriggerEventName="Click"><cal:ActionMessageMethodName="Save"><cal:ParameterValue="{Binding ElementName=Name, Path=Text}"/></cal:ActionMessage></i:EventTrigger></i:Interaction.Triggers></Button></StackPanel></UserControl>

This syntax is Expression Blend friendly.

Databinding

This is automatically binding dependency properties on controls to properties on the ViewModel.

Convention


<TextBoxx:Name="FirstName"/>

Will cause the "Text" property of the TextBox to be bound to the "FirstName" property on the ViewModel.

Explicit


<TextBoxText="{Binding Path=FirstName, Mode=TwoWay}"/>

This is the normal way of binding properties.

Composition

to do

View-Model First

to do

View-First

to do

Window Manager

to do

Event Aggregator

The three different methods on the Event Aggregator are:

publicinterface IEventAggregator {  
    void Subscribe(object instance);  
    void Unsubscribe(object instance);  
    void Publish(object message, Action<System.Action> marshal);  
}

An event can be a simple class such as:

publicclass MyEvent {
    public MyEvent(string myData) {
        this.MyData = myData;
    }

    publicstring MyData { get; privateset; }
}

Updated Wiki: Basic Configuration, Actions and Conventions

$
0
0

Basic Configuration, Actions and Conventions

Open Visual Studio and create a new Silverlight 4 Application called “Caliburn.Micro.Hello”. You don’t need a web site or test project. Add a reference to System.Windows.Interactivity.dll and Caliburn.Micro.dll. You can find them both in the \src\Caliburn.Micro.Silverlight\Bin\Release (or Debug) folder. Delete “MainPage.xaml” and clean up your “App.xaml.cs” so that it looks like this:

namespace Caliburn.Micro.Hello {
    using System.Windows;

    publicpartialclass App : Application {
        public App() {
            InitializeComponent();
        }
    }
}

Since Caliburn.Micro prefers a View-Model-First approach, let’s start there. Create your first VM and call it ShellViewModel. Use the following code for the implementation:

namespace Caliburn.Micro.Hello {
    using System.Windows;

    publicclass ShellViewModel : PropertyChangedBase {
        string name;

        publicstring Name {
            get { return name; }
            set {
                name = value;
                NotifyOfPropertyChange(() => Name);
                NotifyOfPropertyChange(() => CanSayHello);
            }
        }

        publicbool CanSayHello {
            get { return !string.IsNullOrWhiteSpace(Name); }
        }

        publicvoid SayHello() {
            MessageBox.Show(string.Format("Hello {0}!", Name)); //Don't do this in real life :)
        }
    }
}

Notice that the ShellViewModel inherits from PropertyChangedBase. This is a base class that implements the infrastructure for property change notification and automatically performs UI thread marshalling. It will come in handy :)

Now that we have our VM, let’s create the bootstrapper that will configure the framework and tell it what to do. Create a new class named HelloBootstrapper. You can use this tiny bit of code:

namespace Caliburn.Micro.Hello {
    publicclass HelloBootstrapper : BootstrapperBase {
        public HelloBootstrapper() {
            Start();
        }

        protectedoverridevoid OnStartup(object sender, StartupEventArgs e) {
            DisplayRootViewFor<ShellViewModel>();
        }
    }
}

The Bootsrapper allows you to specify the type of “root view model” via the generic method. The “root view model” is a VM that Caliburn.Micro will instantiate and use to show your application. Next, we need to place the HelloBootstrapper somewhere where it will be run at startup. To do that, change your App.xaml to match this:

Silverlight/Phone
<Applicationxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:Caliburn.Micro.Hello"x:Class="Caliburn.Micro.Hello.App"><Application.Resources><local:HelloBootstrapperx:Key="bootstrapper"/></Application.Resources></Application>

WPF
<Applicationxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:Caliburn.Micro.Hello"x:Class="Caliburn.Micro.Hello.App"><Application.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary><local:HelloBootstrapperx:Key="bootstrapper"/></ResourceDictionary></ResourceDictionary.MergedDictionaries></ResourceDictionary></Application.Resources></Application>

All we have to do is place a Caliburn.Micro bootstrapper in the Application.Resources and it will do the rest of the work. Now, run the application. You should see something like this:

ViewNotFound.jpg

Caliburn.Micro creates the ShellViewModel, but doesn’t know how to render it. So, let’s create a view. Create a new Silverlight User Control named ShellView. Use the following xaml:

<UserControlx:Class="Caliburn.Micro.Hello.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><StackPanel><TextBoxx:Name="Name"/><Buttonx:Name="SayHello"Content="Click Me"/></StackPanel></UserControl>

Run the application again. You should now see the UI:

ViewFound.jpg

Typing something in the TextBox will enable the Button and clicking it will show a message:

ViewWithData.jpg

Caliburn.Micro uses a simple naming convention to locate Views for ViewModels. Essentially, it takes the FullName and removes “Model” from it. So, given MyApp.ViewModels.MyViewModel, it would look for MyApp.Views.MyView. Looking at the View and ViewModel side-by-side, you can see that the TextBox with x:Name=”Name” is bound to the “Name” property on the VM. You can also see that the Button with x:Name=”SayHello” is bound to the method with the same name on the VM. The “CanSayHello” property is guarding access to the “SayHello” action by disabling the Button. These are the basics of Caliburn.Micro’s ActionMessage and Conventions functionality. There’s much more to show. But, next time I want to show how we can integrate an IoC container such as MEF.

Referenced Samples

  • Caliburn.Micro.Hello

Updated Wiki: Introduction

$
0
0

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 ~75k assembly (~35k in XAP) that builds for WPF, Silverlight and Windows Phone. 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 short period 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 Action 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 preceded 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– 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. We even support polymorphic subscriptions.
  • 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 its 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 View-Model by 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.
  • ViewModelLocator - Though Caliburn.Micro favors the ViewModel-First approach, we also support View-First by providing a ViewModelLocator with the same mapping semantics as the ViewLocator.
  • WindowManager– This service provides a View-Model-centric way of displaying Windows (ChildWindow in Silverlight, Window in WPF, a custom native-styled host in Windows Phone). 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 Bootstrapper 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 its 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 its 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, and looking forward to seeing your Pull requests.

Updated Wiki: Customizing The Bootstrapper

$
0
0

Customizing The Bootstrapper

In the last part we discussed the most basic configuration for Caliburn.Micro and demonstrated a couple of simple features related to Actions and Conventions. In this part, I would like to explore the Bootstrapper class a little more. Let’s begin by configuring our application to use an IoC container. We’ll use MEF for this example, but Caliburn.Micro will work well with any container. First, go ahead and grab the code from Part 1. We are going to use that as our starting point. Add two additional references: System.ComponentModel.Composition and System.ComponentModel.Composition.Initialization. Those are the assemblies that contain MEF’s functionality.1 Now, let’s create a new Bootstrapper called MefBootstrapper. Use the following code:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Linq;

publicclass MefBootstrapper : BootstrapperBase
{
    private CompositionContainer container;

    public MefBootstrapper()
    {
        Start();
    }

    protectedoverridevoid Configure()
    {
        container = CompositionHost.Initialize(
            new AggregateCatalog(
                AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()
                )
            );

        var batch = new CompositionBatch();

        batch.AddExportedValue<IWindowManager>(new WindowManager());
        batch.AddExportedValue<IEventAggregator>(new EventAggregator());
        batch.AddExportedValue(container);

        container.Compose(batch);
    }

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

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

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

    protectedoverride IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
    }

    protectedoverridevoid BuildUp(object instance)
    {
        container.SatisfyImportsOnce(instance);
    }

    protectedoverridevoid OnStartup(object sender, StartupEventArgs e)
    {
        DisplayRootViewFor<IShell>();
    }
}
Note: We define IShell down below.

That’s all the code to integrate MEF. First, we override the Configure method of the Bootstrapper class. This gives us an opportunity to set up our IoC container as well as perform any other framework configuration we may want to do, such as customizing conventions. In this case, I’m taking advantage of Silverlight’s CompositionHost to setup the CompositionContainer. You can just instantiate the container directly if you are working with .NET. Then, I’m creating an AggregateCatalog and populating it with AssemblyCatalogs; one for each Assembly in AssemblySource.Instance. So, what is AssemblySoure.Instance? This is the place that Caliburn.Micro looks for Views. You can add assemblies to this at any time during your application to make them available to the framework, but there is also a special place to do it in the Bootstrapper. Simply override SelectAssemblies like this:

protectedoverride IEnumerable<Assembly> SelectAssemblies()
{
    returnnew[] {
        Assembly.GetExecutingAssembly()
    };
}

All you have to do is return a list of searchable assemblies. By default, the base class returns the assembly that your Application exists in. So, if all your views are in the same assembly as your application, you don’t even need to worry about this. If you have multiple referenced assemblies that contain views, this is an extension point you need to remember. Also, if you are dynamically loading modules, you’ll need to make sure they get registered with your IoC container and the AssemblySoure.Instance when they are loaded.

After creating the container and providing it with the catalogs, I make sure to add a few Caliburn.Micro-specific services. The framework provides default implementations of both IWindowManager and IEventAggregator. Those are pieces that I’m likely to take dependencies on elsewhere, so I want them to be available for injection. I also register the container with itself (just a personal preference).

After we configure the container, we need to tell Caliburn.Micro how to use it. That is the purpose of the three overrides that follow. “GetInstance” and “GetAllInstances” are required by the framework. “BuildUp” is optionally used to supply property dependencies to instances of IResult that are executed by the framework.


Word to the Wise

While Caliburn.Micro does provide ServiceLocator functionality through the Bootstrapper’s overrides and the IoC class, you should avoid using this directly in your application code. ServiceLocator is considered by many to be an anti-pattern. Pulling from a container tends to obscure the intent of the dependent code and can make testing more complicated. In future articles I will demonstrate at least one scenario where you may be tempted to access the ServiceLocator from a ViewModel. I’ll also demonstrate some solutions.2

Besides what is shown above, there are some other notable methods on the Bootstrapper. You can 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. The last override, DisplayRootView, is unique. Let’s look at how it is implemented in Bootstrapper<TRootModel>

protectedoverridevoid DisplayRootView() 
{
    var viewModel = IoC.Get<TRootModel>();
#if SILVERLIGHT
    var view = ViewLocator.LocateForModel(viewModel, null, null);
    ViewModelBinder.Bind(viewModel, view, null);

    var activator = viewModel as IActivate;
    if (activator != null)
        activator.Activate();

    Application.RootVisual = view;
#else
    IWindowManager windowManager;

    try
    {
        windowManager = IoC.Get<IWindowManager>();
    }
    catch
    {
        windowManager = new WindowManager();
    }

    windowManager.Show(viewModel);
#endif
}

The Silverlight version of this method resolves your root VM from the container, locates the view for it and binds the two together. It then makes sure to “activate” the VM if it implements the appropriate interface. The WPF version does the same thing by using the WindowManager class, more or less. DisplayRootView is basically a convenience implementation for model-first development. If you don’t like it, perhaps because you prefer view-first MVVM, then this is the method you want to override to change that behavior.

v1.1 Changes
In v1.1 we removed the DisplayRootView override and placed it's functionality in a helper method named DisplayRootViewFor. The generic bootstrapper now calls this method from the OnStartup override. To change this behavior, just override OnStartup, and instead of calling the base implementation, write your own activation code. This provides better support for splash screens, login screens and access to startup parameters.

Now that you understand all about the Bootstrapper, let’s get our sample working. We need to add the IShell interface. In our case, it’s just a marker interface. But, in a real application, you would have some significant shell-related functionality baked into this contract. Here’s the code:

publicinterface IShell
{
}

Now, we need to implement the interface and decorate our ShellViewModel with the appropriate MEF attributes:

[Export(typeof(IShell))]
publicclass ShellViewModel : PropertyChangedBase, IShell
{
   ...implementation is same as before...
}

Finally, make sure to update your App.xaml and change the HelloBootstrapper to MefBootstrapper. That’s it! Your up and running with MEF and you have a handle on some of the other key extension points of the Bootstrapper as well.

Using Caliburn.Micro in Office and WinForms Applications

Caliburn.Micro can be used from non-Xaml hosts. In order to accomplish this, you must follow a slightly different procedure, since your application does not initiate via the App.xaml. Instead, create a custom boostrapper by inheriting from BoostrapperBase (the non-generic version). When you inherit, you should pass "false" to the base constructor's "useApplication" parameter. This allows the bootstrapper to properly configure Caliburn.Micro without the presence of a Xaml application instance. All you need to do to start the framework is create an instance of your Bootstrapper and call the Start() method. Once the class is instantiated, you can use Caliburn.Micro like normal, probably by invoke the IWindowManager to display new UI.

Referenced Samples

  • Caliburn.Micro.HelloMef

Footnotes

  1. If you are using .NET, you will only need to reference System.ComponentModel.Composition.
  2. I’m quite guilty of this myself, but I’m trying to be more conscious of it. I’m also excited to see that modern IoC containers as well as Caliburn.Micro provide some very nice ways to avoid this situation.

Updated Wiki: All About Actions

$
0
0

All About Actions


We briefly introduced actions in Pt. 1, but there is so much more to know. To begin our investigation, we’ll take our simple “Hello” example and see what it looks like when we explicitly create the actions rather than use conventions. Here’s the Xaml:


<UserControlx:Class="Caliburn.Micro.Hello.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"xmlns:cal="http://www.caliburnproject.org"><StackPanel><TextBoxx:Name="Name"/><ButtonContent="Click Me"><i:Interaction.Triggers><i:EventTriggerEventName="Click"><cal:ActionMessageMethodName="SayHello"/></i:EventTrigger></i:Interaction.Triggers></Button></StackPanel></UserControl>

As you can see, the Actions feature leverages System.Windows.Interactivity for it’s trigger mechanism. This means that you can use anything that inherits from System.Windows.Interactivity.TriggerBase to trigger the sending of an ActionMessage.1 Perhaps the most common trigger is an EventTrigger, but you can create almost any kind of trigger imaginable or leverage some common triggers already created by the community. ActionMessage is, of course, the Caliburn.Micro-specific part of this markup. It indicates that when the trigger occurs, we should send a message of “SayHello.” So, 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. ActionMessage bubbles through the Visual Tree searching for a target instance that can handle it. If a target is found, but does not have a “SayHello” method, the framework will continue to bubble until it finds one, throwing an exception if no “handler” is found.2 This bubbling nature of ActionMessage comes in handy in a number of interesting scenarios, Master/Details being a key use case. Another important feature to note is Action guards. When a handler is found for the “SayHello” message, it will check to see if that class also has either a property or a method named “CanSayHello.” If you have a guard property and your class implements INotifyPropertyChanged, then the framework will observe changes in that property and re-evaluate the guard accordingly. We’ll discuss method guards in further detail below.

Action Targets

Now you’re probably wondering how to specify the target of an ActionMessage. Looking at the markup above, there’s no visible indication of what that target will be. So, where does that come from? Since we used a Model-First approach, when Caliburn.Micro (hereafter CM) created the view and bound it to the ViewModel using the ViewModelBinder, it set this up for us. Anything that goes through the ViewModelBinder will have its action target set automatically. But, you can set it yourself as well, using the attached property Action.Target. Setting this property positions an ActionMessage “handler” in the Visual Tree attached to the node on with you declare the property. It also sets the DataContext to the same value, since you often want these two things to be the same. However, you can vary the Action.Target from the DataContext if you like. Simply use the Action.TargetWithoutContext attached property instead. One nice thing about Action.Target is that you can set it to a System.String and CM will use that string to resolve an instance from the IoC container using the provided value as its key. This gives you a nice way of doing View-First MVVM if you so desire. If you want Action.Target set and you want Action/Binding Conventions applied as well, you can use the Bind.Model attached property in the same way.

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:


publicclass MefBootstrapper : BootstrapperBase
{
    //same as beforeprotectedoverridevoid OnStartup(object sender, StartupEventArgs e)
    {
        Application.RootVisual = new ShellView();
    }

    //same as before
} 

Because we are using View-First, we’ve inherited from the non-generic Bootstrapper. The MEF configuration is the same as seen previously, so I have left that out for brevity’s sake. The only other thing that is changed is how the view gets created. In this scenario, we simply override OnStartup, instantiate the view ourselves and set it as the RootVisual (or call Show in the case of WPF). Next, we’ll slightly alter how we are exporting our ShellViewModel, by adding an explicitly named contract:


[Export("Shell", typeof(IShell))]
publicclass ShellViewModel : PropertyChangedBase, IShell
{
    //same as before
} 

Finally, we will alter our view to pull in the VM and perform all bindings:


<UserControlx:Class="Caliburn.Micro.ViewFirst.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:cal="http://www.caliburnproject.org"cal:Bind.Model="Shell"><StackPanel><TextBoxx:Name="Name"/><Buttonx:Name="SayHello"Content="Click Me"/></StackPanel></UserControl>

Notice the use of the Bind.Model attached property. This resolves our VM by key from the IoC container, sets the Action.Target and DataContext and applies all conventions. I thought it would be nice to show how View-First development is fully supported with CM, but mainly I want to make clear the various ways that you can set targets for actions and the implications of using each technique. Here’s a summary of the available attached properties:
  • Action.Target– Sets both the Action.Target property and the DataContext property to the specified instance. String values are used to resolve an instance from the IoC container.
  • Action.TargetWithoutContext– Sets only the Action.Target property to the specified instance. String values are used to resolve an instance from the IoC container.
  • Bind.Model– View-First - Set’s the Action.Target and DataContext properties to the specified instance. Applies conventions to the view. String values are used to resolve an instance from the IoC container. (Use on root nodes like Window/UserControl/Page.)
  • Bind.ModelWithoutContext - View-First - Set’s the Action.Target to the specified instance. Applies conventions to the view. (Use inside of DataTemplate.)
  • View.Model– ViewModel-First – Locates the view for the specified VM instance and injects it at the content site. Sets the VM to the Action.Target and the DataContext. Applies conventions to the view.

Action Parameters

Now, let’s take a look at another interesting aspect of ActionMessage: Parameters. To see this in action, let’s switch back to our original ViewModel-First bootstrapper, etc. and begin by changing our ShellViewModel to look like this:


using System.ComponentModel.Composition;
using System.Windows;

[Export(typeof(IShell))]
publicclass ShellViewModel : IShell
{
    publicbool CanSayHello(string name)
    {
        return !string.IsNullOrWhiteSpace(name);
    }

    publicvoid SayHello(string name)
    {
        MessageBox.Show(string.Format("Hello {0}!", name));
    }
} 

There are a few things to note here. First, we are now working with a completely POCO class; no INPC goop here. Second, we have added an input parameter to our SayHello method. Finally, we changed our CanSayHello property into a method with the same inputs as the action, but with a bool return type. Now, let’s have a look at the Xaml:


<UserControlx:Class="Caliburn.Micro.HelloParameters.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"xmlns:cal="http://www.caliburnproject.org"><StackPanel><TextBoxx:Name="Name"/><ButtonContent="Click Me"><i:Interaction.Triggers><i:EventTriggerEventName="Click"><cal:ActionMessageMethodName="SayHello"><cal:ParameterValue="{Binding ElementName=Name, Path=Text}"/></cal:ActionMessage></i:EventTrigger></i:Interaction.Triggers></Button></StackPanel></UserControl>

Our markup now has one modification: We declared the parameter as part of the ActionMessage using an ElementName Binding. You can have any number of parameters you desire. Value is a DependencyProperty, so all the standard binding capabilities apply to parameters. Did I mention you can do all this in Blend?


ActionsInBlend.jpg


One thing that is nice about this is that every time the value of a parameter changes, 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. Go ahead and run the application. You’ll see that it behaves the same as in previous examples.

In addition to literal values and Binding Expressions, there are a number of helpful “special” values that you can use with parameters. These allow you a convenient way to access common contextual information:
  • $eventArgs– Passes the Trigger’s EventArgs or input parameter to your Action. Note: This will be null for guard methods since the trigger hasn’t actually occurred.
  • $dataContext– Passes the DataContext of the element that the ActionMessage is attached to. 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.
  • $source– The actual FrameworkElement that triggered the ActionMessage to be sent.
  • $view - The view (usually a UserControl or Window) that is bound to the ViewModel.
  • $executionContext - The actions's execution context, which contains all the above information and more. This is useful in advanced scenarios.
  • $this - The actual ui element to which the action is attached.
You must start the variable with a “$” but the name is treated in a case-insensitive way by CM. These can be extended through adding values to MessageBinder.SpecialValues.

Note: Using Special Values like $this or a Named Element
When you don't specify a property, CM uses a default one, which is specified by the particular control convention. For button, that property happens to be "DataContext", while a TextBox defaults to Text, a Selector to SelectedItem, etc. The same happens when using a reference to another named control in the View instead of $this. The following: <Button cal:Message.Attach="Click = MyAction(someTextBox)" /> causes CM to pass the Text contained in the TextBox named "someTextBox" to MyAction. The reason why the actual control is never passed to the action is that VMs should never directly deal with UI elements, so the convention discourages it. Note, however, that the control itself could easily be accessed anyway using the extended syntax (based on System.Windows.Interactivity) to populate the parameters, or customizing the Parser.

Word to the Wise
Parameters are a convenience feature. They are very powerful and can help you out of some tricky spots, but they can be easily abused. Personally, I only use parameters in the simplest scenarios. One place where they have worked nicely for me is in login forms. Another scenario, as mentioned previously is Master/Detail operations.


Now, do you want to see something truly wicked? Change your Xaml back to this:


<UserControlx:Class="Caliburn.Micro.HelloParameters.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><StackPanel><TextBoxx:Name="Name"/><Buttonx:Name="SayHello"Content="Click Me"/></StackPanel></UserControl>

Running the application will confirm for you that CM’s conventions even understand ActionMessage parameters. We’ll discuss conventions a lot more in the future, but you should be happy to know that these conventions are case-insensitive and can even detect the before-mentioned “special” values.

Action Bubbling

Now, lets look at a simple Master/Detail scenario that demonstrates ActionMessage bubbling, but let’s do it with a shorthand syntax that is designed to be more developer friendly. We’ll start by adding a simple new class named Model:


using System;

publicclass Model
{
    public Guid Id { get; set; }
}

And then we’ll change our ShellViewModel to this:


using System;
using System.ComponentModel.Composition;

[Export(typeof(IShell))]
publicclass ShellViewModel : IShell
{
    public BindableCollection<Model> Items { get; privateset; }

    public ShellViewModel()
    {
        Items = new BindableCollection<Model>{
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() },
            new Model { Id = Guid.NewGuid() }
        };
    }

    publicvoid Add()
    {
        Items.Add(new Model { Id = Guid.NewGuid() });
    }

    publicvoid Remove(Model child)
    {
        Items.Remove(child);
    }
}

Now our shell has a collection of Model instances along with the ability to add or remove from the collection. Notice that the Remove method takes a single parameter of type Model. Now, let’s update the ShellView:


<UserControlx:Class="Caliburn.Micro.BubblingAction.ShellView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:cal="http://www.caliburnproject.org"><StackPanel><ItemsControlx:Name="Items"><ItemsControl.ItemTemplate><DataTemplate><StackPanelOrientation="Horizontal"><ButtonContent="Remove"cal:Message.Attach="Remove($dataContext)"/><TextBlockText="{Binding Id}"/></StackPanel></DataTemplate></ItemsControl.ItemTemplate></ItemsControl><ButtonContent="Add"cal:Message.Attach="Add"/></StackPanel></UserControl>

Message.Attach

The first thing to notice is that we are using a more Xaml-developer-friendly mechanism for declaring our ActionMessages. The Message.Attach property is backed by a simple parser which takes its textual input and transforms it into the full Interaction.Trigger/ActionMessage that you’ve seen previously. If you work primarily in the Xaml editor and not in the designer, you’re going to like Message.Attach. Notice that neither Message.Attach declarations specify which event should send the message. If you leave off the event, the parser will use the ConventionManager to determine the default event to use for the trigger. In the case of Button, it’s Click. You can always be explicit of coarse. Here’s what the full syntax for our Remove message would look like if we were declaring everything:


<ButtonContent="Remove"cal:Message.Attach="[Event Click] = [Action Remove($dataContext)]"/>

Suppose we were to re-write our parameterized SayHello action with the Message.Attach syntax. It would look like this:


<ButtonContent="Click Me"cal:Message.Attach="[Event Click] = [Action SayHello(Name.Text)]"/>

But we could also leverage some smart defaults of the parser and do it like this:


<ButtonContent="Click Me"cal:Message.Attach="SayHello(Name)"/>

You can specify literals as parameters as well and even declare multiple actions by separating them with a semicolon:


<ButtonContent="Let's Talk"cal:Message.Attach="[Event MouseEnter] = [Action Talk('Hello', Name.Text)]; [Event MouseLeave] = [Action Talk('Goodbye', Name.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. 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. Please don’t abuse this.


If you haven’t already, run the application. 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. So, for example, you can pump TextBox.Text into a System.Double parameter without any fear of a casting issue.

So, we’ve discussed using Interaction.Triggers with ActionMessage, including the use of Parameters with literals, element bindings3 and special values. We’ve discussed the various ways to set the action target depending on your needs/architectural style: Action.Target, Action.TargetWithoutContext, Bind.Model or View.Model. We also saw an example of the bubbling nature of ActionMessage and demoed it using the streamlined Message.Attach syntax. All along the way we’ve looked at various examples of conventions in action too. Now, there’s one final killer feature of ActionMessage we haven’t discussed yet…Coroutines. But, that will have to wait until next time.

Referenced Samples

  • Caliburn.Micro.HelloExplicitAction
  • Caliburn.Micro.ViewFirst
  • Caliburn.Micro.HelloParameters
  • Caliburn.Micro.BubblingAction

Footnotes

  1. Currently, the full version of Caliburn is not based on System.Windows.Interactivity. Caliburn’s trigger mechanism was around long before Blend’s. You may notice a shocking similarity in the markup. That said, Caliburn v2.0 will be migrated to use the Blend model in the near future.
  2. Actually, if no handler is found, before an exception is thrown, the framework will check the current DataContext to see if it has the requested method. This seamed like a reasonable fallback behavior.
  3. One important detail about ElementName Bindings that I didn’t mention…It doesn’t work with WP7 currently. Due to the fact that WP7 is based on a version of Silverlight 3 which had an incomplete implementation of DependencyObject/DependencyProperty, the infrastructure is not present to make this work in any sort of sane way. However, parameter literals and special values still work as described along with all the rest of the ActionMessage features.

Updated Wiki: Working with Windows Phone

$
0
0

Working with Windows Phone

In version 1.0 we had pretty good support for building apps for WP7, but in v1.1 we’ve taken things up a notch. Let’s look at the same HelloWP7 sample that we did previously, but see how it’s been updated to take advantage of our improved tombstoning, launcher/chooser support and strongly typed navigation. You’ll also notice that the code is cleaner overall.

Bootstrapper

Here’s the cleaned up boostrapper in v1.1.

publicclass HelloWP7Bootstrapper : PhoneBootstrapperBase {  
    PhoneContainer container;  
  
    public HelloWP7Bootstrapper() {
        Start();
    }

    protectedoverridevoid Configure() {  
        container = new PhoneContainer(RootFrame);

        if (!Execute.InDesignMode)
            container.RegisterPhoneServices();

        container.PerRequest<MainPageViewModel>();  
        container.PerRequest<PivotPageViewModel>();  
        container.PerRequest<TabViewModel>();  
  
        AddCustomConventions();  
    }  
  
    staticvoid AddCustomConventions() {  
        //ellided  
    }  
  
    protectedoverrideobject GetInstance(Type service, string key) {  
        return container.GetInstance(service, key);  
    }  
  
    protectedoverride IEnumerable<object> GetAllInstances(Type service) {  
        return container.GetAllInstances(service);  
    }  
  
    protectedoverridevoid BuildUp(object instance) {  
        container.BuildUp(instance);  
    }  
}  

There are two things to notice here. First, we’ve removed all the manual Caliburn.Micro service configuration and pushed it into the SimpleContainer. That gives you one line of code to configure the framework if you are using the OOTB container. Speaking of which, we now provide the SimpleContainer officially in the Caliburn.Micro.Extensions assembly. That helps you get started faster. You can always plug your own in, of coarse. In addition to the simplified configuration, notice that the ViewModels for pages are no longer registered using a string key. For v1.1 our ViewModelLocator has been re-implemented to pull VMs from the container by Type rather than key. It now follows the exact same naming strategies as the ViewLocator (but in reverse) and even derives possible interface names so that it resolves VMs from the container correctly. This both improves the consistency of ViewModel location as well as makes the configuration simpler.

The boostrapper is added to your App.xaml as always:

<Applicationx:Class="Caliburn.Micro.HelloWP7.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:Caliburn.Micro.HelloWP7"><Application.Resources><local:HelloWP7Bootstrapperx:Key="bootstrapper"/></Application.Resources></Application>

Important Note About App.xaml.cs

If you create your WP7 application using a standard Visual Studio template, the generated App.xaml.cs file will have a lot of code in it. The purpose of this code is to set up the root frame for the application and make sure everything gets initialized properly. Of course, 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). So, you don't need both. When using CM's PhoneBootstrapper, be sure to clear out all the code from the App.xaml.cs file except for the call to InitializeComponent in the constructor.

INavigationService

Let’s review what CM’s INavigationService does for you. First, remember that WP7 enforces a View-First approach to UI at the platform level. Like it or not, the platform is going to create pages at will and the Frame control is going to conduct your application thusly. You don’t get to control that and there are no extensibility points, unlike the Silverlight version of the navigation framework. Rather than fight this, I’m going to recommend embracing the View-First approach for Pages in WP7, but maintaining a Model-First composition strategy for the sub-components of those pages and a Model-First approach to coding against the navigation system. In order to bridge this gap, I’ve enabled the INavigationService 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 Type of the VM that should be attached to the page being navigated to. Pull that VM by Type out of the container.
  • If a VM is found, use the ViewModelBinder to connect the Page to the located ViewModel.
  • Examine the Page’s QueryString. Look for properties on the VM that match the QueryString parameters and inject them, performing the necessary type coercion.
  • If the ViewModel implements the IActivate interface, call its Activate method.

When Navigating Away From a Page
  • Detect whether the associated ViewModel implements the IGuardClose interface.
  • If IGuardClose is implemented and the app is not being tombstoned or closed, invoke the CanClose method and use its result to optionally cancel page navigation.1
  • If the ViewModel can close and implements the IDeactivate interface, call it’s Deactivate method. Always pass “false” to indicate that the VM should deactivate, but not necessarily close. This is because the phone may be deactivating, but not actually tombstoning or closing. There’s no way to know.

The behavior of the navigation service allows the correct VM to be hooked up to the page, allows that VM to be notified that it is being navigated to (IActivate), allows it to prevent navigation away from the current page (IGuardClose) and allows it to clean up after itself on navigation away, tombstoning or normal “closing” of the application (IDeactivate). All these interfaces (and a couple more) are implemented by the Screen class. If you prefer not to inherit from Screen, you can implement any of the interfaces individually of coarse. They provide a nice View-Model-Centric, 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.

These hooks into phone navigation enable a really smooth way of interacting with the phone’s navigation lifecycle. But now that we have an improved ViewModelLocator that matches exactly the ViewLocator and works on types, we can take things further. In v1.1 we’ve introduced support for strongly-typed navigation. Here’s what the new MainPageViewModel from the sample looks like using this new feature:

publicclass MainPageViewModel {  
    readonly INavigationService navigationService;  
  
    public MainPageViewModel(INavigationService navigationService) {  
        this.navigationService = navigationService;  
    }  
  
    publicvoid GotoPageTwo() {  
        navigationService.UriFor<PivotPageViewModel>()  
            .WithParam(x => x.NumberOfTabs, 5)  
            .Navigate();  
    }  
}  

This allows you to specify a ViewModel to navigate to along with the query string parameters. Since this all happens using generics and lambdas, you can never miss-type a page Uri or mess up your query strings….and refactoring will work beautifully.

For the sake of completeness, here’s the page that will be bound to MainPageViewModel:

<phone:PhoneApplicationPagex:Class="Caliburn.Micro.HelloWP7.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"SupportedOrientations="Portrait"Orientation="Portrait"shell:SystemTray.IsVisible="True"><GridBackground="Transparent"><Grid.RowDefinitions><RowDefinitionHeight="Auto"/><RowDefinitionHeight="*"/></Grid.RowDefinitions><StackPanelGrid.Row="0"Margin="24,24,0,12"><TextBlockText="WP7 Caliburn.Micro"Style='{StaticResource PhoneTextNormalStyle}'/><TextBlockText='Main Page'Margin='-3,-8,0,0'Style='{StaticResource PhoneTextTitle1Style}'/></StackPanel><GridGrid.Row='1'><Buttonx:Name='GotoPageTwo'Content='Goto Page Two'/></Grid></Grid><phone:PhoneApplicationPage.ApplicationBar><shell:ApplicationBarIsVisible='True'><shell:ApplicationBar.Buttons><cal:AppBarButtonIconUri='ApplicationIcon.png'Text='Page Two'Message='GotoPageTwo'/></shell:ApplicationBar.Buttons></shell:ApplicationBar></phone:PhoneApplicationPage.ApplicationBar></phone:PhoneApplicationPage>

There’s really nothing new here in v1.1. But I just wanted to remind you that Caliburn.Micro supports Actions on the AppBar as long as you use CM’s AppBarButton and AppBarMenuItem :)

IPhoneService

The IPhoneService wraps the phone’s frame and provides access to important information and events. We had this service in v1.0 but we’ve expanded it in v1.1 to expose a better event model. Those familiar with WP7 know that the phone has a series of events that fire in different circumstances: Launching, Activated, Deactivated and Closing. Unfortunately, these events obscure whether the phone is actually resurrecting from a tombstoned state or simply continuing execution. The current SDK does not make it easy for the developer to actually determine this, so Caliburn.Micro does the heavy lifting for you and provides the following event model:
  • Launching - Occurs when a fresh instance of the application is launching.
  • Activated - Occurs when a previously paused/tombstoned app is resumed/resurrected.
  • Deactivated - Occurs when the application is being paused or tombstoned.
  • Closing - Occurs when the application is closing.
  • Continuing - Occurs when the app is continuing from a temporarily paused state.
  • Continued - Occurs after the app has continued from a temporarily paused state.
  • Resurrecting - Occurs when the app is "resurrecting" from a tombstoned state.
  • Resurrected - Occurs after the app has "resurrected" from a tombstoned state.

Using these new events, you can more intelligently make decisions about whether or not you need to restore data. In the forthcoming Mango release, the platform will provide us information on whether the app is continuing or resurrecting. However, developers working with Caliburn.Micro can have that information now and when Mango arrives, we’ll update our implementation to use the new bits. Your code won’t have to change.

Tombstoning

As you might imagine, our new tombstoning mechanism takes advantage of the new events so that it can more reliably and accurately save/restore important data. Let’s have a look at the PivotPageViewModel to see how it interacts with the tombstoning mechanism.

publicclass PivotPageViewModel : Conductor<IScreen>.Collection.OneActive {  
    readonly Func<TabViewModel> createTab;  
  
    public PivotPageViewModel(Func<TabViewModel> createTab) {  
        this.createTab = createTab;  
    }  
  
    publicint NumberOfTabs { get; set; }  
  
    protectedoverridevoid OnInitialize() {  
        Enumerable.Range(1, NumberOfTabs).Apply(x => {  
            var tab = createTab();  
            tab.DisplayName = "Item " + x;  
            Items.Add(tab);  
        });  
  
        ActivateItem(Items[0]);  
    }  
}  

The PivotPageViewModel will receive the number of pivot items to create through it’s NumberOfTabs property, which is pushed in from the query string, as mentioned above. It will then add these items to the conductor and activate the first one. If you’re familiar with the Pivot and CM’s previous sample, you’ll notice that our PivotFix is gone. Pivot has a horrible bug that will crash your application if you try to set the SelectedItem or SelectedIndex to an item 3 or greater from either end of the pivot collection, while the Pivot itself is not visible. This makes it really hard to restore this control from a tombstoned state because you have to set the value at the exact right time. Previously we used a PivotFix hack to work around the control’s bug, but the new tombstoning mechanism is powerful and extensible enough to just make it work. You’ll notice that there are no attributes describing tombstoning behavior. They’ve been removed in favor of a poco model inspired by Fluent NHibhernate. If you would rather have the attributes, you can actually build them on top of the new system. The new system is also more reliable than previously and has a lot more options for storage. Let’s see the class that describes the tombstoning behavior for PivotPageViewModel:

publicclass PivotPageModelStorage : StorageHandler<PivotPageViewModel> {  
    publicoverridevoid Configure() {  
        this.ActiveItemIndex()  
            .InPhoneState()  
            .RestoreAfterViewLoad();  
    }  
}  

All you have to do to make a class participate in tombstoning is to inherit from StorageHandler<T>. The PhoneContainer will auto-register anything of this type in the assembly. Just override the Configure method and declare the tombstoning instructions. I’ve created some extension methods for common scenarios. Here’s what the above declaration states:
  • Persist the Conductor’s ActiveItem’s index
  • Store the index in PhoneState
  • Restore the value after the associated view has been loaded.

Let’s look at the storage handler for the TabViewModel to see some more options:

publicclass TabViewModelStorage : StorageHandler<TabViewModel> {  
    publicoverridevoid Configure() {  
        Id(x => x.DisplayName);  
  
        Property(x => x.Text)  
            .InPhoneState()  
            .RestoreAfterActivation();  
    }  
}  

Here we are specifying an Id because we actually need to persist multiple instances of the same VM. When we restore, we’ll need to know how to map the properties back. We’re also storing the data in PhoneState, but this time we’re not waiting for the view to load, but just waiting for the TabViewModel to be activated by its owning Conductor.

Out of the box, we also support storing data in AppSettings. For example, if you wanted to same tab to be selected across application restarts not just when tombstoned, you could define the PivotPageModelStorage like this:

publicclass PivotPageModelStorage : StorageHandler<PivotPageViewModel> {  
    publicoverridevoid Configure() {  
        this.ActiveItemIndex()  
            .InAppSettings()  
            .RestoreAfterViewLoad();  
    }  
}  

Pretty easy? All this works by collaborating with the IoC container and keying off of the new event model exposed by the IPhoneService. It’s pretty powerful and extensible. You can add your own storage mechanism or define your own restore timing. You can even implement IStorageHandler directly to write completely custom code on a class by class basis. You could easily add a version that inspected classes for custom attributes and built up the configuration, if you like the attribute model better. You can also store whole instances, not just their properties, and have them rehydrated properly and available for ctor injection.

Note About Restore Timing

If you want the data to be restored as soon as the object is created, leave off the timing specifier, ie. RestoreAfterViewLoad. The default is to restore the data immediately.

Launchers and Choosers

Launchers and Choosers are painful to work with if you want to do MVVM. In v1.0 we provided a solution to this. I wasn’t happy with its implementation…it was unpredictable in certain scenarios. Once we established the new phone events, better IoC integration and new tombstoning mechanism, I realized I could build a better launcher/chooser system. Let’s take a look at the updated version of TabViewModel in order to see how it works:

publicclass TabViewModel : Screen, IHandle<TaskCompleted<PhoneNumberResult>> {  
    string text;  
    readonly IEventAggregator events;  
  
    public TabViewModel(IEventAggregator events) {  
        this.events = events;  
    }  
  
    publicstring Text {  
        get { return text; }  
        set {  
            text = value;  
            NotifyOfPropertyChange(() => Text);  
        }  
    }  
  
    publicvoid Choose() {  
        events.RequestTask<PhoneNumberChooserTask>();  
    }  
  
    publicvoid Handle(TaskCompleted<PhoneNumberResult> message) {  
        MessageBox.Show("The result was " + message.Result.TaskResult, DisplayName, MessageBoxButton.OK);  
    }  
  
    protectedoverridevoid OnActivate() {  
        events.Subscribe(this);  
        base.OnActivate();  
    }  
  
    protectedoverridevoid OnDeactivate(bool close) {  
        events.Unsubscribe(this);  
        base.OnDeactivate(close);  
    }  
}  

The most significant architectural change I made was to re-implement the launcher/chooser mechanism to work on top of the IEventAggregator. Take a look at the Choose method. The RequestTask method is just an extension method of the IEventAggregator that publishes a special event that the framework is subscribed to. The framework then starts the task. When it’s completed, the framework publishes an event TaskCompleted<T> where T is the result the the chooser returns. You can register for this in the same VM that published the chooser event or in an entirely different one if you like. In the case of our sample, we have 5 TabViewModels that can launch the same chooser. That’s probably not normal, but you can handle this situation in three ways. In our case, the VMs are in a Conductor, and only one of them can be active at a time, so we just Subscribe/Unsubscribe based on the Screen lifecycle so that only the active VM will receive the result. This is a version of the Latch pattern. The second way to handle this is through the event state. When you call the RequestTask method you can pass a state object which you can use for identification purposes later. Yes, this will be present even if the chooser causes a tombstone event. The final way is to have a single object that registers for the completed event, decoupling the launching from the completion. Thus multiple VM could launch the same chooser, but only one class would handle the result.

IWindowManager

The IWindowManager was actually in v1.0, as a last minute addition. It’s a really easy way to show native-looking, custom message boxes or modal dialogs. You can also use it to show popups. There is a whole topic devoted to this in the docs.

Referenced Samples

  • Caliburn.Micro.HelloWP7

Footnotes

  1. Even though the IGuardClose interface was designed to handle async scenarios, you must use it synchronously in WP7. This is due to a flaw in the design of Silverlight Navigation Framework which doesn’t account for async shutdown scenarios.

Updated Wiki: IResult and Coroutines

$
0
0

IResult and Coroutines

Previously, I mentioned that there was one more compelling feature of the Actions concept called Coroutines. If you haven’t heard that term before, here’s what wikipedia1 has to say:

In computer science, coroutines are program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations. Coroutines are well-suited for implementing more familiar program components such as cooperative tasks, iterators,infinite lists and pipes.

Here’s one way you can thing about it: Imagine being able to execute a method, then pause it’s execution on some statement, go do something else, then come back and resume execution where you left off. This technique is extremely powerful in task-based programming, especially when those tasks need to run asynchronously. For example, let’s say we have a ViewModel that needs to call a web service asynchronously, then it needs to take the results of that, do some work on it and call another web service asynchronously. Finally, it must then display the result in a modal dialog and respond to the user’s dialog selection with another asynchronous task. Accomplishing this with the standard event-driven async model is not a pleasant experience. However, this is a simple task to accomplish by using coroutines. The problem…C# doesn’t implement coroutines natively. Fortunately, we can (sort of) build them on top of iterators.

There are two things necessary to take advantage of this feature in Caliburn.Micro: First, implement the IResult interface on some class, representing the task you wish to execute; Second, yield instances of IResult from an Action2. Let’s make this more concrete. Say we had a Silverlight application where we wanted to dynamically download and show screens not part of the main package. First we would probably want to show a “Loading” indicator, then asynchronously download the external package, next hide the “Loading” indicator and finally navigate to a particular screen inside the dynamic module. 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.Collections.Generic;
using System.ComponentModel.Composition;

[Export(typeof(ScreenOneViewModel))]
publicclass ScreenOneViewModel
{
    public IEnumerable<IResult> GoForward()
    {
        yieldreturn Loader.Show("Downloading...");
        yieldreturnnew LoadCatalog("Caliburn.Micro.Coroutines.External.xap");
        yieldreturn Loader.Hide();
        yieldreturnnew ShowScreen("ExternalScreen");
    }
}

First, notice that the Action “GoForward” has a return type of IEnumerable<IResult>. This is critical for using coroutines. The body of the method has four yield statements. Each of these yields is returning an instance of IResult. The first is a result to show the “Downloading” indicator, the second to download the xap asynchronously, the third to hide the “Downloading” message and the fourth to show a new screen from the downloaded xap. After each yield statement, the compiler will “pause” the execution of this method until that particular task completes. The first, third and fourth tasks are synchronous, while the second is asynchronous. But the yield syntax allows you to write all the code in a sequential fashion, preserving the original workflow as a much more readable and declarative structure. To understand a bit more how this works, have a look at the IResult interface:

publicinterface IResult
{
    void Execute(CoroutineExecutionContext context);
    event EventHandler<ResultCompletionEventArgs> Completed;
}

It’s a fairly simple interface to implement. Simply write your code in the “Execute” method and be sure to raise the “Completed” event when you are done, whether it be a synchronous or an asynchronous task. Because coroutines occur inside of an Action, we provide you with an ActionExecutionContext useful in building UI-related IResult implementations. This allows the ViewModel a way to declaratively state its intentions in controlling the view without having any reference to a View or the need for interaction-based unit testing. Here’s what the ActionExecutionContext looks like:

publicclass ActionExecutionContext
{
    public ActionMessage Message;
    public FrameworkElement Source;
    publicobject EventArgs;
    publicobject Target;
    public DependencyObject View;
    public MethodInfo Method;
    public Func<bool> CanExecute;
    publicobjectthis[string key];
}

And here’s an explanation of what all these properties mean:
  • Message – The original ActionMessage that caused the invocation of this IResult.
  • Source – The FrameworkElement that triggered the execution of the Action.
  • EventArgs – Any event arguments associated with the trigger of the Action.
  • Target – The class instance on which the actual Action method exists.
  • View – The view associated with the Target.
  • Method – The MethodInfo specifying which method to invoke on the Target instance.
  • CanExecute – A function that returns true if the Action can be invoked, false otherwise.
  • Key Index: A place to store/retrieve any additional metadata which may be used by extensions to the framework.

Bearing that in mind, 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. Here’s the implementation:

using System;
using System.Windows;
using System.Windows.Controls;

publicclass Loader : IResult
{
    readonlystring message;
    readonlybool hide;

    public Loader(string message)
    {
        this.message = message;
    }

    public Loader(bool hide)
    {
        this.hide = hide;
    }

    publicvoid Execute(CoroutineExecutionContext context)
    {
        var view = context.View as FrameworkElement;
        while(view != null)
        {
            var busyIndicator = view as BusyIndicator;
            if(busyIndicator != null)
            {
                if(!string.IsNullOrEmpty(message))
                    busyIndicator.BusyContent = message;
                busyIndicator.IsBusy = !hide;
                break;
            }

            view = view.Parent as FrameworkElement;
        }

        Completed(this, new ResultCompletionEventArgs());
    }

    publicevent EventHandler<ResultCompletionEventArgs> Completed = delegate { };

    publicstatic IResult Show(string message = null)
    {
        returnnew Loader(message);
    }

    publicstatic IResult Hide()
    {
        returnnew Loader(true);
    }
}

See how I took advantage of context.View? This opens up a lot of possibilities while maintaining separation between the view and the view model. Just to list a few interesting things you could do with IResult implementations: show a message box, show a VM-based modal dialog, show a VM-based Popup at the user’s mouse position, play an animation, show File Save/Load dialogs, place focus on a particular UI element based on VM properties rather than controls, etc. Of course, one of the biggest opportunities is calling web services. Let’s look at how you might do that, but by using a slightly different scenario, dynamically downloading a xap:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Linq;

publicclass LoadCatalog : IResult
{
    staticreadonly Dictionary<string, DeploymentCatalog> Catalogs = new Dictionary<string, DeploymentCatalog>();
    readonlystring uri;

    [Import]
    public AggregateCatalog Catalog { get; set; }

    public LoadCatalog(string relativeUri)
    {
        uri = relativeUri;
    }

    publicvoid Execute(CoroutineExecutionContext context)
    {
        DeploymentCatalog catalog;

        if(Catalogs.TryGetValue(uri, out catalog))
            Completed(this, new ResultCompletionEventArgs());
        else
        {
            catalog = new DeploymentCatalog(uri);
            catalog.DownloadCompleted += (s, e) =>{
                if(e.Error == null)
                {
                    Catalogs[uri] = catalog;
                    Catalog.Catalogs.Add(catalog);
                    catalog.Parts
                        .Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly)
                        .Where(assembly => !AssemblySource.Instance.Contains(assembly))
                        .Apply(x => AssemblySource.Instance.Add(x));
                }
                else Loader.Hide().Execute(context);

                Completed(this, new ResultCompletionEventArgs {
                    Error = e.Error,
                    WasCancelled = false
                });
            };

            catalog.DownloadAsync();
        }
    }

    publicevent EventHandler<ResultCompletionEventArgs> Completed = delegate { };
}

In case it wasn’t clear, this sample is using MEF. Furthermore, we are taking advantage of the DeploymentCatalog created for Silverlight 4. You don’t really need to know a lot about MEF or DeploymentCatalog to get the takeaway. Just take note of the fact that we wire for the DownloadCompleted event and make sure to fire the IResult.Completed event in its handler. This is what enables the async pattern to work. We also make sure to check the error and pass that along in the ResultCompletionEventArgs. Speaking of that, here’s what that class looks like:

publicclass ResultCompletionEventArgs : EventArgs
{
    public Exception Error;
    publicbool WasCancelled;
}

Caliburn.Micro’s enumerator checks these properties after it get’s called back from each IResult. If there is either an error or WasCancelled is set to true, we stop execution. You can use this to your advantage. Let’s say you create an IResult for the OpenFileDialog. You could check the result of that dialog, and if the user canceled it, set WasCancelled on the event args. By doing this, you can write an action that assumes that if the code following the Dialog.Show executes, the user must have selected a file. This sort of technique can simplify the logic in such situations. Obviously, you could use the same technique for the SaveFileDialog or any confirmation style message box if you so desired. My favorite part of the LoadCatalog implementation shown above, is that the original implementation was written by a CM user! Thanks janoveh for this awesome submission! As a side note, one of the things we added to the CM project site is a “Recipes” section. We are going to be adding more common solutions such as this to that area in the coming months. So, it will be a great place to check for cool plugins and customizations to the framework.

Another thing you can do is create a series of IResult implementations built around your application’s shell. That is what the ShowScreen result used above does. Here is its implementation:

using System;
using System.ComponentModel.Composition;

publicclass ShowScreen : IResult
{
    readonly Type screenType;
    readonlystring name;

    [Import]
    public IShell Shell { get; set; }

    public ShowScreen(string name)
    {
        this.name = name;
    }

    public ShowScreen(Type screenType)
    {
        this.screenType = screenType;
    }

    publicvoid Execute(CoroutineExecutionContext context)
    {
        var screen = !string.IsNullOrEmpty(name)
            ? IoC.Get<object>(name)
            : IoC.GetInstance(screenType, null);

        Shell.ActivateItem(screen);
        Completed(this, new ResultCompletionEventArgs());
    }

    publicevent EventHandler<ResultCompletionEventArgs> Completed = delegate { };

    publicstatic ShowScreen Of<T>()
    {
        returnnew ShowScreen(typeof(T));
    }
}

This bring up another important feature of IResult. Before CM executes a result, it passes it through the IoC.BuildUp method allowing your container the opportunity to push dependencies in through the properties. This allows you to create them normally within your view models, while still allowing them to take dependencies on application services. In this case, we depend on IShell. You could also have your container injected, but in this case I chose to use the IoC static class internally. As a general rule, you should avoid pulling things from the container directly. However, I think it is acceptable when done inside of infrastructure code such as a ShowScreen IResult.

Other Usages

Out-of-the-box Caliburn.Micro can execute coroutines automatically for any action invoked via an ActionMessage. However, there are times where you may wish to take advantage of the coroutine feature directly. To execute a coroutine, you can use the static Coroutine.BeginExecute method.


I hope this gives some explanation and creative ideas for what can be accomplished with IResult. Be sure to check out the sample application attached. There’s a few other interesting things in there as well.

Referenced Samples

  • Caliburn.Micro.Coroutines
Note: Please be sure to run the sample as an out-of-browser application.

Footnotes

  1. 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. Scrolling down to the section on C#…Caliburn was listed! Fun stuff.
  2. 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.

Updated Wiki: The Event Aggregator

$
0
0

The Event Aggregator

Caliburn.Micro comes pre-bundled with an Event Aggregator, conveniently called EventAggregator. For those unfamiliar, an Event Aggregator is a service that provides the ability to publish an object from one entity to another in a loosely based fashion. Event Aggregator is actually a pattern and it's implementation can vary from framework to framework. For Caliburn.Micro we focused on making our Event Aggregator implementation simple to use without sacrificing features or flexibility.

Getting Started

As previously mentioned we provide an implementation of Event Aggregator for you. This implementation implements the IEventAggregator interface, however, you can provide your own implementation if required. Please take a moment to familiarize yourself with the IEventAggregator signature.

publicinterface IEventAggregator {
    	bool HandlerExistsFor(Type messageType);
    	void Subscribe(object subscriber);
    	void Unsubscribe(object subscriber);
    	void Publish(object message, Action<Action> marshal);
    }

Creation and Lifecycle

To use the EventAggregator correctly it must exist as an application level service. This is usually achieved by creating an instance of EventAggregator as a Singleton. We recommend that you use Dependency Injection to obtain a reference to the instance although we do not enforce this. The sample below details how to create an instance of EventAggregator, add it to the IoC container included with Caliburn.Micro (although you are free to use any container you wish) and request it in a ViewModel.

// Creating the EventAggregator as a singleton.publicclass Bootstrapper : BootstrapperBase {
        privatereadonly SimpleContainer _container =
            new SimpleContainer();

         // ... Other Bootstrapper Configprotectedoverridevoid Configure(){
            _container.Singleton<IEventAggregator, EventAggregator>();
        }

        // ... Other Bootstrapper Config
    }

    // Acquiring the EventAggregator in a viewModel.publicclass FooViewModel {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
        }
    }

Note that we are utilizing the Bootstrapper, and specifically the Configure method, in the code above. There is no requirement to wire up the EventAggregator in a specific location, simply ensure it is created before it is first requested.

Publishing Events

Once you have obtained a reference to the EventAggregator instance you are free to begin publishing Events. An Event or message as we call it to distinguish between .Net events, can be any object you like. There is no requirement to build your Events in any specific fashion. As you can see in the sample below the Publish method can accept any entity that derives from System.Object and will happily publish it to any interested subscribers.

publicclass FooViewModel {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;

            _eventAggregator.Publish(newobject());
            _eventAggregator.Publish("Hello World");
            _eventAggregator.Publish(22);
        }
    }

Publishing using a custom thread Marshal

By convention, the EventAggregator publishes on the UI thread (using PublishOnUIThread() method). You can override this per publish. Consider the following code below which publishes the message supplied on a background thread.

    _eventAggregator.Publish(newobject(), action => {
                    Task.Factory.StartNew(action);
                });

Subscribing To Events

Any entity can subscribe to any Event by providing itself to the EventAggregator via the Subscribe method. To keep this functionality easy to use we provide a special interface (IHandle<T>) which marks a subscriber as interested in an Event of a given type.

    IHandle<TMessage> : IHandle {
	    void Handle<TMessage>(TMessage message);
    }

Notice that by implementing the interface above you are forced to implement the method Handle(T message) were T is the type of message you have specified your interest in. This method is what will be called when a matching Event type is published.

publicclass FooViewModel : IHandle<object> {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        publicvoid Handle(object message) {
            // Handle the message here.
        }
    }

You may be wondering about IHandle<T> implementing IHandle. IHandle is a Marker Interface used internally to assist with reflecting over various IHandle Implementations, you do not need to work with this interface directly.

Subscribing To Many Events

It is not uncommon for a single entity to want to listen for multiple event types. Because of our use of generics this is as simple as adding a second IHandle<T> interface to the subscriber. Notice that Handle method is now overloaded with the new Event type.

publicclass FooViewModel : IHandle<string>, IHandle<bool> {
        privatereadonly IEventAggregator _eventAggregator;
    
        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }
    
        publicvoid Handle(string message) {
            // Handle the message here.
        }

        publicvoid Handle(bool message) {
            // Handle the message here.
        }
    }

Polymorphic Subscribers

Caliburn.Micro's EventAggregator honors polymorphism. When selecting handlers to call, the EventAggregator will fire any handler who's Event type is assignable from the Event being sent. This results in a lot of flexibility and helps reuse.

publicclass FooViewModel : IHandle<object>, IHandle<string> {
        privatereadonly IEventAggregator _eventAggregator;
    
        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
            _eventAggregator.Publish("Hello");
        }
    
        publicvoid Handle(object message) {
            // This will be called
        }

        publicvoid Handle(string message) {
            // This also
        }
    }

In the example above, because String is derived from System.Object both handlers will be called when a String message is published.

Querying Handlers

When a subscriber is passed to the EventAggregator it is broken down into a special object called a Handler and a weak reference is maintained. We provide a mechanism to query the EventAggregator to see if a given Event type has any handlers, this can be useful in specific scenarios were at least one handler is assumed to exist.

publicclass FooViewModel : IHandle<object> {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        publicvoid Handle(object message){
            if (_eventAggregator.HandlerExistsFor(typeof(SpecialMessageEvent))){
                _eventAggregator.Publish(new SpecialEventMessage(message));
            }
        }
    }

Coroutine Aware Subscribers

If you are using the EventAggregator with Caliburn.Micro as opposed to on it's own via Nuget, access to Coroutine support within the Event Aggregator becomes available. Coroutines are supported via the IHandleWithCoroutine<T> Interface.

publicinterface IHandleWithCoroutine<TMessage> : IHandle {
		IEnumerable<IResult> Handle(TMessage message);
    }

The code below utilizes Coroutines with the EventAggregator. In this instance Activate will be fired asynchronously, Do work however, will not be called until after Activate has completed.

publicclass FooViewModel : Screen, IHandleWithCoroutine<EventWithCoroutine> {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public IEnumerable<IResult> Handle(EventWithCoroutine message) {
            yieldreturn message.Activate();
            yieldreturn message.DoWork();
        }
    }

publicclass EventWithCoroutine {
        public IResult Activate() {
            returnnew TaskResult(Task.Factory.StartNew(() => {
                    // Activate logic
                }));
        }

        public IResult DoWork() {
            returnnew TaskResult(Task.Factory.StartNew(() => {
                // Do work logic
            }));
        }
    }

Task Aware Subscribers

Caliburn.Micro also provides support for task based subscribers where the asynchronous functionality of Coroutines is desired but in a more light weight fashion. To utilize this functionality implement the IHandleWithTask<T> Interface, seen below.

publicinterface IHandleWithTask<TMessage> : IHandle {
        Task Handle(TMessage message);
    }

Any subscribers that implement the above interface can then handle events in Task based manner.

publicclass FooViewModel : Screen, IHandleWithTask<object> {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public Task Handle(object message) {
            return Task.Factory.StartNew(() => message);
        }
    }

Unsubscribing and Leaks

The problem with standard .Net events is that they are prone to memory leaks. We avoid this situation by maintaining a weak reference to subscribers. If the only thing that references a subscriber is the EventAggregator then it will be allowed to go out of scope and ultimately be garbage collected. However, we still provide an explicit way to unsubscribe to allow for conditional handling as seen below.

publicclass FooViewModel : Screen, IHandle<object> {
        privatereadonly IEventAggregator _eventAggregator;

        public FooViewModel(IEventAggregator eventAggregator) {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        publicvoid Handle(object message) {
            // Handle the message here.
        }

        protectedoverridevoid OnActivate() {
            _eventAggregator.Subscribe(this);
            base.OnActivate();
        }

        protectedoverridevoid OnDeactivate(bool close) {
            _eventAggregator.Unsubscribe(this);
            base.OnDeactivate(close);
        }
    }

In the code above Screen is used to expose lifecycle events on the ViewModel. More on this can be found on this in the Screens, Conductors and Composition article on this wiki.

Custom Result Handling

In more complex scenarios it may be required to override the default handling of Handlers which have results. In this instance it is possible to swap out the existing implementation in favour of your own. First we create a new handler type.

    IHandleAndReturnString<T> : IHandle {
	    string Handle<T>(T message);
    }

Next we create our new result processor. This can be configured in the bootstrapper.

var standardResultProcesser = EventAggregator.HandlerResultProcessing;
    EventAggregator.HandlerResultProcessing = (target, result) =>
    {
        var stringResult = result asstring;
        if (stringResult != null)
            MessageBox.Show(stringResult);
        else
            standardResultProcesser(target, result);
    };

Now, any time an event is processed returns a string, it will be captured and displayed in a MessageBox. The new handler falls back to the default implementations in cases were the result is not assignable from string. It is extremely important to note however, this feature was not designed for request / response usage, treating it as such will most definitely create bottle necks on publish.

Updated Wiki: The Service Locator

$
0
0

The Service Locator

Caliburn.Micro comes pre-bundled with a static Service Locator called IoC. For those unfamiliar, a Service Locator is an entity that can provide another entity with service instances, usually based on some type or key. Service Locator is actually a pattern and is related to Inversion of Control. Many consider Service Locator to be an anti-pattern but like all patterns it has its use cases.

Getting Started

IoC is the static entity used for Service Location in Caliburn.Micro, this enables IoC to work with static entites such as dependency properties with ease. The public definition of IoC is shown below.

publicstaticclass IoC {
	publicstatic Func<Type, string, object> GetInstance;
        publicstatic Func<Type, IEnumerable<object>> GetAllInstances;
        publicstatic Action<object> BuildUp;

        publicstatic T Get<T>(string key = null);
	publicstatic IEnumerable<T> GetAll<T>();
}

As you can see above much of the functionality of IoC is dependant on the consumer providing it. In most cases the relevant methods required map directly to methods provided by all Dependency Injection containers (although the name and functionality may differ).

Injecting IoC with functionality.

Caliburn.Micro requires IoC to work correctly because it takes advantage of it at a framework level. As a non optional service we provide an extensibility point directly on the Bootstrapper for the purposes of injecting functionality into IoC. Below, the sample uses Caliburn.Micro's own SimpleContainer to inject functionality into IoC.

publicclass CustomBootstrapper : Bootstrapper {
	private SimpleContainer _container = new SimpleContainer();
		
	//...protectedoverrideobject GetInstance(Type service, string key) {
		return _container.GetInstance(service, key);
	}

	protectedoverride IEnumerable<object> GetAllInstances(Type service) {
		return _container.GetAllInstances(service);
	}

	protectedoverridevoid BuildUp(object instance) {
		_container.BuildUp(instance);
	}

	//...
}

By mapping your chosen dependency container to IoC, Calburn.Micro can take advantage of any service bindings made on the container via Service Location.

Using IoC in your application

As stated at the outset Service Location, apart from a few specific areas, is considered by many to be an anti pattern; in most cases you will want to make use of your dependency injection container. Many problems that Service Locator solves can be fixed without it by planning out your applications composition; refer to Screens, Conductors & Composition for more information on composition.

However, if you still require Service Location, IoC makes it easy. The code below shows how to use the service locator to retrieve or inject instances with services.

Getting a single service

IoC supports the retrieval of a single type by type or type and key. Key-based retrieval is not supported by all dependency injection containers, because of this the key param is optional.

var windowManager = IoC.Get<IWindowManager>();
var windowManager = IoC.Get<IWindowManager>("windowManager");

Getting a collection of services

Requesting a collection of services is also supported by IoC. The return type is IEnumerable T where T is the type of service requested. LINQ can be used to filter the final collection but be aware that at this point any entity in the collection will have already been instantiated.

var viewModelCollection = IoC.GetAll<IViewModel>();
var viewModel = IoC.GetAll<IViewModel>()
                              .FirstOrDefault(vm => vm.GetType() == typeof(ShellViewModel));

Injecting an instance with services

IoC supports the injection of services into a given instance. The mechanics of how this is done is left to the implementation injected into the Action<Instance> BuildUp field. There are various places in the framework were this is used to inject functionality into entities that were created externally to the dependency container mapped to IoC.

var viewModel = new LocallyCreatedViewModel();
IoC.BuildUp(viewModel);

Closed Task: Update Documentation [346]

$
0
0
Some parts of the documentation are out-dated and others are missing completely.

Edited Issue: Exception on Toggle in/out of view in Win8.1 [354]

$
0
0
In Win8.1, if you have a ItemsControl derived type that removes and re-adds some portion of its children and you're using the c:View.Model binding on a data template to convert child view models into their views, you hit an issue.

The issue is this. The OnModelChanged event gets fired every time that the child appears or disappears, which in and of itself would not be bad. However, the following pattern is then employed ...

1. Use the ViewLocator to get the view.
2. Set the ContentProperty on the target to the view.

The problem lies in that the ViewLocator attempts to use a cached view by default and that view is still part of the visual tree. Therefore, the setting the content property fails because the view is already the child of another element in the tree.

This has been tested with several Syncfusion controls that employ this visual hiding/reshowing.

The workaround is to add this override, which trades out the exception (and failure to display the view-model with instead a memory increase as the view keeps being created.


public override object GetView(object context = null) {
return null;
}

New Post: [WP8] Implementing simple loading overlay

New Post: showdialog and databinding

$
0
0
hi, I show a dialog that binds some simple properties, but I experience a problem whereby the dialog pops up and then the fields bind after the popup this isn't very nice to look at. Is there someway slightly the displaying of the dialog until I have bound data?

I could just navigate to a page, but I find navigated back from the page to my calling page to be very slow as I have a lot of data items loaded in the calling pages/pivots and I find that a dialog navigates back instantly as I guess it does not need to re-render the calling page.

New Post: Single instance wpf app

$
0
0
BladeWise wrote:
Check the latest source code from the project I posted above. I added a CM sample.
I've added you solution to my CM project and can display the arguments passed from a second instance. I have a method in my ShellViewModel to handle the arguments. How do I get a reference to my ShellViewModel object to be able to use that method?

Created Unassigned: Error using updated library in Win 8.1 [357]

$
0
0
I have updated the Caliburn.Micro, Caliburn.Micro.Extensions, Callisto and Windows.UI.Interactivity dll's in my windows store 8.1 project ( Dev machine is windows 8.1 pro ) , and I have pulled them in from the

Caliburn.Micro v1.5.2 Snapshot\bin\WinRT\ directory.

My app is giving me this error still with the updated DLL's imported.

Error 1 Cannot find type System.Collections.CollectionBase in module mscorlib.dll.

Any ideas what might be causing this?


I am referencing the EventAggregator objects.

Commented Unassigned: Error using updated library in Win 8.1 [357]

$
0
0
I have updated the Caliburn.Micro, Caliburn.Micro.Extensions, Callisto and Windows.UI.Interactivity dll's in my windows store 8.1 project ( Dev machine is windows 8.1 pro ) , and I have pulled them in from the

Caliburn.Micro v1.5.2 Snapshot\bin\WinRT\ directory.

My app is giving me this error still with the updated DLL's imported.

Error 1 Cannot find type System.Collections.CollectionBase in module mscorlib.dll.

Any ideas what might be causing this?


I am referencing the EventAggregator objects.
Comments: Forget this... I didn't realize there was an alpha 2. I have installed that and voila errors are gone. I just have to redo my publish statements. Thanks!!!

Closed Unassigned: Error using updated library in Win 8.1 [357]

$
0
0
I have updated the Caliburn.Micro, Caliburn.Micro.Extensions, Callisto and Windows.UI.Interactivity dll's in my windows store 8.1 project ( Dev machine is windows 8.1 pro ) , and I have pulled them in from the

Caliburn.Micro v1.5.2 Snapshot\bin\WinRT\ directory.

My app is giving me this error still with the updated DLL's imported.

Error 1 Cannot find type System.Collections.CollectionBase in module mscorlib.dll.

Any ideas what might be causing this?


I am referencing the EventAggregator objects.

New Post: Binding control event to System.Action or any other delegate?

$
0
0
Question as above, short example:

ViewModel
public System.Action SomeAction;

private void Function1() {}
private void Function2() {}

//in code somewhere
SomeAction = new System.Action(Function1);
//in code elsewhere
SomeAction = new System.Action(Function2);
View
xmlns:cm="..."
<Button ...
    cm:Message.Attach=[Event Click] = [Action SomeAction()] />
Is something like that possible with Caliburn.Micro? I think it will be suitable in some kind of situations, but code like above throws an exception (MethodNotFound), which is not much a surprise.

Or maybe I should never lead to this kind of situation because it's a cause of bad design?
Viewing all 1760 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>