Wednesday, March 14, 2012

Understanding the Portable Library by Chasing ICommand (3 of 3)

Part 1: Creating the Portable Library
Part 2: Portability in Silverlight and WPF: a Tale of Type Forwarders
Part 3: Portability in Metro: A CLR and WinRT Love Affair (this post)

Portability in Metro: A CLR and WinRT Love Affair

In this series we’ve covered the portable library and reviewed how it allows you to create assemblies that can be shared without recompilation across multiple platforms. You created a portable assembly with a view model and a command in it, then successfully integrated it in a WPF and a Silverlight project. Now it’s time to code for the future and go Metro.

Create a new C# Metro application using the blank page template. Reference the PortableCommandLibrary project. Open the BlankPage.xaml file and drop in the same XAML you used for WPF and Silverlight. First, fix up a reference:

xmlns:portable="using:PortableCommandLibrary"

Next, add the XAML inside of the main grid:

<Grid.DataContext>
    <portable:ViewModel/>
</Grid.DataContext>
<Button Content="{Binding Text}" Command="{Binding ClickCommand}"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        Margin="10"/>

Now compile, deploy, and run the application. It will work just as it did for WPF and Silverlight.

First the button:

portablelibmetro1

Then the disabled button:

portablelibmetro2

What’s interesting for this example is that you know when you want to wire a command, you have to use a completely separate namespace from Silverlight. In fact, the namespace implies that you are accessing a WinRT component that is part of the operating system and not even a managed object. How do we pull that off with an assembly that isn’t modified?

To begin the journey, start with the assembly that is referenced directly by the portable library. This is System.Windows.dll only this time you’ll inspect it in the .NETCore folder, which is the smaller profile allowed for Metro applications. Once again, the assembly contains no implementation. Opening the manifest, you will find a series of type forwarders. This time the ICommand interface is redirected to System.ObjectModel.dll.

portablelibmetro3

What’s next? You guessed it. Pop open the System.ObjectModel.dll assembly and you’ll find:

portablelibmetro4

So there it is … but there’s a problem. When you specify your own command implementation, you have to reference the Windows.UI.Xaml.Input namespace. So how will this reference work? This is where Metro works a little bit of magic.

It turns out the platform maintains an internal table that maps CLR namespaces to the WinRT equivalents. This allows seamless integration between the types. For example, the CLR may be exposed to the type Windows.Foundation.Uri when dealing with a WinRT component. When this happens, it automatically maps this to the .NET System.Uri. When the CLR passes System.Uri to a WinRT component, it is converted to a Windows.Foundation.Uri reference.

In our case, the code references:

System.Windows.Input.ICommand

The platform will automatically map this to the WinRT counterpart,

Windows.UI.Xaml.Input.ICommand

This is a very powerful feature because it enables compatibility between legacy code and the new runtime with minimal effort on the part of the developer. If your type maps to an actual object that can have an activated instance, rather than just an interface, the CLR will automatically instantiate a Runtime Callable Wrapper (RCW) to proxy calls to the underlying WinRT (essentially COM) component.

The whole portable path looks like this in the end:

portablelibmetro5

If you want to see the “projected” types you can use ildasm.exe with the /project switch and in theory, if you run this against one of the .WinMD files (such as Windows.UI.Xaml.Input.WinMD) located in %windir%\winmetdata you should see .NET projected types instead of Windows Runtime types … I have yet to get this to work but if you have, please post the details here.

And that’s it – you’ve learned how to create an assembly that is completely portable between .NET Framework 4.5 (WPF), Silverlight 4.0 and 5.0, and Windows 8 Metro, and learned a bit about how it works by chasing down ICommand under the covers. Hopefully this helps with understanding the library and also with planning out how to map future projects that need to share code between existing implementations and future Metro targets.

6 comments:

  1. What about IObservable?
    Portable library is a joke.

    ReplyDelete
  2. Nice! By the way, IObservable for portable library: http://channel9.msdn.com/Shows/Going+Deep/Bart-De-Smet-Inside-Rx-V2-Beta

    ReplyDelete
  3. Thanks Jeremy for the clarification. Do you have an idea how we could use interfaces like IValueConverter in a portable library, where the method signature is different between .NET 4 and Metro? Is it possible, that there will be type forwarders, which can also map between the different signatures?

    ReplyDelete
    Replies
    1. Unfortunately there is a subset of interfaces to work with, so that is one I don't think you can make portable. There may be those forwarders in the future but right now you have to work with the subset that is exposed.

      Delete
    2. I have done some work on Portability of ValueConverters across WP7, WinRt, Touch and Droid. Sadly the portable class library team won't support them, so what I do is:

      - create a IMvxValueConverter in my portable library

      - use that MvxValueConverter directly in Touch and Droid (it's my code so I can just use it!)

      - wrap it to convert it to Windows IValueConverter in WP7 using: https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WindowsPhone/Platform/Converters/MvxNativeValueConverter.cs

      - wrap it to convert it to Xaml IValueCovnerter in WinRT using: https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.WinRT/Platform/Converters/MvxNativeValueConverter.cs

      Here's an example of a wrapped converter:

      https://github.com/slodge/MvvmCross/blob/vnext/Sample%20-%20Tutorial/Tutorial/Tutorial.UI.WinRT/NativeConverters/StringLengthValueConverter.cs

      It's not perfect - e.g. the language info isn't really pushed through, but it works in my apps.

      Delete
  4. Why not take this to the logical extreme and have everything reference a single redirector DLL which forward to the appropriate DLL depending on which platform the program is on ?

    ReplyDelete