Saturday, June 7, 2014

Upgrading Caliburn Micro from 1.5 to 2.0 for a WPF application

I am happy to see that the Model-View-View Model (MVVM) framework Caliburn Micro is adopting the Portable Class Library approach in version 2.0, and I thought that now would also be a good time to upgrade some of my existing applications from Caliburn Micro 1.5 to version 2.0.

So far, this seems to be a painless experience, but I ran to a few issues after upgrading to 2.0 via NuGet. I'd thought I share these experiences in case someone else is facing the same issues.

The application in question is a WPF application, and it is using the Managed Extensibility Framework (MEF) for bootstrapping the application.

When trying to build the application immediately after the upgrade, I ran into a compilation error in the class definition line of my MEF bootstrapper class:

public MefBootstrapper : Bootstrapper<IShell>

error CS0246: The type or namespace name 'Bootstrapper' could not be found (are you missing a using directive or an assembly reference?)

Searching the Caliburn Micro documentation, I found that the bootstrapper base class is now called ... BootstrapperBase! And it is non-generic.

OK, this change seems easy:

public MefBootstrapper : BootstrapperBase

Hooray, the application builds! Let's run it. And wait ... and wait ... and wait ... but it never starts?

Of course! Since the bootstrapper base class is now non-generic, there is no way for the bootstrapper to know which View it should start displaying, unless it is explicitly being told which View it should display. And according to this documentation page, the fix is easy; just add

DisplayRootViewFor<IShell>();

to the overridden OnStartup method.

Let's try to run the application again. Still no sight of the root window. What is missing?

Looking more closely into the bootstrapper implementation example here, the constructor also seems to perform some initialization operation. This was not necessary with the pre-2.0 Caliburn Micro versions, most likely because the base class constructor performed this operation instead. But with 2.0 it seems to be necessary to also implement the bootstrapper constructor explicitly. According to the migration instructions here it also seems like the Start method has been renamed Initialize. So, here goes:

public MefBootstrapper() { Initialize(); }

Run the application, and ... Yes! The start-up window appears!

To summarize: when upgrading a WPF/MEF application from using Caliburn Micro version 1.5 or earlier to version 2.0, make sure the following details are in place:

  • The MEF based bootstrapper class should inherit from BootstrapperBase.
  • The root view will be displayed by calling DisplayRootViewFor<> in the overridden OnStartup method.
  • The bootstrapper default constructor must be explicitly defined, and call the Initialize() method.

EDIT

Ironically, when I started upgrading my second application I faced a completely different compilation error:

error CS1501: No overload for method 'Publish' takes 1 arguments

But this issue has a quick and easy fix, as pointed out in the migration instructions:

EventAggregator.Publish now takes an action to marshal the event. Use EventAggregator.PublishOnUIThread for the existing behaviour.


Thursday, June 5, 2014

PCL Tips and Tricks: Extension methods to the rescue

The introduction of Portable Class Libraries, PCL, considerably facilitates the sharing of functionality across managed Microsoft and Xamarin platforms. I am very enthusiastic about the PCL concept, and I have ported several .NET Framework open source projects to Portable Class Libraries. Along the way I have stumbled across various issues of making .NET Framework source code PCL compliant. In this mini-series I'll share my experiences and workarounds to facilitate the transfer to PCL for other developers.

Now, here is a very common scenario when porting .NET class libraries to PCL:

public class SomeClassToPort
{
    public static byte[] SomeMethodToPort()
    {
        var stream = new MemoryStream();
        var writer = new StreamWriter(stream);
        writer.Write("Result: {0}, {1}", 10, "Hello");
        // some more writer operations ...

        writer.Close();   // Non-existent in PCL
        stream.Close();   // Non-existent in PCL

        return stream.ToArray();
    }
}

Correct, the Close() method is not available for streams, readers or writers in Portable Class Libraries! Since the method in practice only executes Dispose(), the Close() method has been excluded from PCL. Trying to build this code yields the following error:

error CS1061: 'System.IO.MemoryStream' does not contain a definition for 'Close' and no extension method 'Close' accepting a first argument of type 'System.IO.MemoryStream' could be found (are you missing a using directive or an assembly reference?)

Dispose() is generally available in PCL, so one solution is to replace all Close() calls with Dispose(). For those wanting to avoid the hassle and maintenance issues of replacing (potentially numerous) Close() calls, there is however a workaround.


Extension methods are a perfect fit for dealing with a scenario like the above. You can just add a static class with an arbitrary name to the PCL library, with code similar to this:

public static class CloseExtensions
{
    public static void Close(this Stream stream)
    {
        stream.Dispose();
    }

    public static void Close(this TextWriter writer)
    {
        writer.Dispose();
    }
}

Now, the compiler will "translate" stream.Close() into the call CloseExtensions.Close(stream) (analogously for the StreamWriter object) and the Portable Class Library can then build successfully. The extension methods perform practically the same operations as the Close() methods in the .NET Framework. Subsequent calls to Dispose() have no effect, so this implementation should be fail-safe. 

There is also no risk of name clashes; even when the PCL library is used in a .NET application the original Close() methods and the corresponding extension methods have completely different signatures (they can just be expressed equivalently in the code).

The Close() methods are probably the most obvious examples where this extension method technique can be applied, but whenever a PCL profile is not including an instance method of one type, the same technique may be applied. Other examples from the top of my head are:
  • several methods from the Type class which are not available for Windows 8 (f.k.a. Store or Metro apps); much of this functionality has been replaced by TypeInfo instance and extension methods,
  • Stream.BeginRead, Stream.EndRead, Stream.BeginWrite and Stream.EndWrite; again not available for Windows 8 but can be simulated using extension methods.