Friday, March 9, 2012

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

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

Portability in Silverlight and WPF: a Tale of Type Forwarders

In the last post, I walked through creating a portable assembly that will target Silverlight 4.0 and above, .NET Framework 4.5, and Windows 8 Metro. In the assembly were a few classes that handled commands and property change notification for a simple view model. In this post I’ll show you how to reference the assembly in Silverlight and WPF and explain why it works.

The first step is to create a new Silverlight 5.0 project (just using that because it’s the latest version, I know the library will technically support 4.0). Just make a simple application (no need to have a web project as well). The project will be created with the default App.xaml and MainPage.xaml. In the solution explorer, right-click on the References node and add a reference to the PortableCommandLibrary project. Now open up the XAML for the main page. At the top, add a namespace declaration for the view model:

xmlns:portable="clr-namespace:PortableCommandLibrary;assembly=PortableCommandLibrary"

Next, paste the following XAML inside the main grid called LayoutRoot (you’ll use the exact same snippet for all of the projects in this series).

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

Now compile and run the application. You should see something like this:

portablelibsilverlight1

If you follow the instructions, you should end up with this:

portablelibsilverlight2

I’d love to show you how this worked with the portable library, but the answer is pretty boring. As I showed you in the last post, the portable assembly points to an ICommand interface that lives in the System.Windows.Input namespace in the System.Windows.dll assembly. If you peek inside Silverlight’s assemblies and run ildasm.exe you’ll see:

portablelibsilverlight3

The reference can be visualized like this:

portablelibsilverlight4

Really no magic at all – Silverlight is a lowest common denominator here. So let’s try something a little more interesting. Create a new WPF project and reference the same portable library. Add the same namespace declaration to the MainWindow.xaml file and drop in the XAML inside the Grid tag. Run the application – look familiar?

portablelibwpf1

Click it.

portablelibwpf2

OK, so it works the same way, but we noted earlier that the ICommand interface lives someplace different. How does this work? If you recall, the reference to System.Windows.dll in the portable library was tagged as retargetable. This tells the runtime that the target may really exist somewhere else. In fact, if you look at the references available for the .NET Framework 4.5 (here’s a tip: forget that old %windir%\Microsoft.NET\Framework\ stuff … instead, try the newer %programdir%\Reference Assemblies\Microsoft\Framework\.NETFramework\ folder), you’ll find there is a System.Windows.dll file. Pop it open with ildasm.exe and you’ll see there is no implementation in the file, only metadata. Read the manifest and you’ll come across this little gem:

portablelibwpf3

Ah-hah! The portable library people have been planning this for longer than most readers suspect. There’s a nice reference now that politely invites the CLR to look somewhere else for the implementation, specifically in System.dll. If you open that assembly, you’ll see the interface is indeed defined there. So, what really happened looks a little like this for the .NET Framework 4.5:

portablelibwpf4

If you’re turning pale at the thought of so many hops, don’t get worried. These tables are loaded into memory and represent very miniscule lookups. The portable team assured me that any performance cost due to a little indirection is negligible.

What I love about the approach is that it uses a lot of pieces that have been already in place but in a clever way that gives us this powerful functionality of using the same assembly in Silverlight (web) or WPF (desktop). In the next post, we’ll take it even further and see how it relates to the brand new platform of the Windows Runtime (WinRT) for a Windows 8 Metro application. How on earth do we go from this to an unmanaged code base?

3 comments:

  1. Very interesting and great concept...thanks for the explanation.

    However, I am trying this with WPF/XAML/C# on Windows 8 and VS 2012 Ultimate but cannot get the 'portable' clr-namespace to see the ViewModel class. If I test with another class in same namespace but one that does NOT inherit from INotifyProperyChanged then it finds it fine.

    If you have a link to the full code, maybe I could then find out where I'm going wrong - any help appreciated and all the best for Xmas and the New post-Mayan Year!!

    ReplyDelete
    Replies
    1. You can see a full example in the source code for my book. It's available for download here (look at Chapter 9) http;//windows8applications.codeplex.com/

      Delete
  2. Hi Jeremy - thanks for you prompt response - Chapter 9 helped. I noticed that your PortableWintellog project targeted .Net 4.5 and Windows Store. I reconfigured my portable library to the same and the MainWindow.xaml now found the ViewModel class (in the portable library).

    However, there seems to be some ambiguity here - if I then add SilverLight as a target, the XAML fails. Likewise if I add Windows Phone 7 or higher. But, if I just target .Net 4.5, Windows Store and Windows Phone 8 it works.

    I know that it's a bit sad (on my part) debugging code on Christmas Eve but the wife's sound asleep and the kids are trashing the kitchen in a last minute Christmas baking effort. This was bugging me though and I'm glad to have found a work around, so appreciate you help. Will read your book once I know a bit more about MVVM. I'm also hoping to apply the 'portable library' concept you've outlined to Entity Framework...

    Thanks again and a Merry Christmas!

    ReplyDelete