Colin Eberhardt's Technology Adventures

Using ObservableCollection with WinRT (via a little shim!)

October 6th, 2011

WInRT introduces a new interface for collection change notification, IObservableVector, which means ObservableCollection no longer works with this framework. In this blog post I will show how you can use ObservableCollection, via a simple adapter, within you WInRT applications.

<ItemsControl ItemsSource="{Binding Path=MyCollection, Converter={StaticResource ObservableCollectionAdapter}}"/>

Developers are slowly starting to get their heads around the differences between the old (Silverlight, WPF, WP7) and the new – WinRT. Whilst the it has a familiar feel, with the UI defined in XAML and an API that is very similar to .NET, there are a great many differences. For an overview of some of these differences see:

A number of developers have discovered that while WinRT has the ObservableCollection class, when bound to the UI, lists / grids fail to update as items are added / removed. This is because while the same interfaces are being used for collections (ILIst, IEnumerable etc …), there is a new interface for notification of collection changes, IObservableVector. Because ObservableCollection implements IEnumerable its contents will be rendered in the UI, however it implements the ‘old’ interface for change notification – INotifyCollectionChanged.

The WinRT SDK samples include classes that implement IObservableVector to demonstrate binding with collection change handling, however, what if you have some old code using ObservableCollection that you want to port over? Or, what if you want to code-share between Silverlight and WInRT?

A few days ago I saw a blog post by Avi Pilosof where he created a class that implements both the INotifyCollectionChanged and IObservableVector interfaces. This will certainly do the job, however there is a simpler way …

Using the Gang of Four Adapter pattern, we can create a class which wraps ObservableCollection, to implement IObservableVector:

/// <summary>
/// Adapts an ObservableCollection to implement IObservableVector
/// </summary>
public class ObservableCollectionShim<T> : IObservableVector<T>
{
  private ObservableCollection<T> _adaptee;
 
  public ObservableCollectionShim(ObservableCollection<T> adaptee)
  {
    _adaptee = adaptee;
    _adaptee.CollectionChanged += Adaptee_CollectionChanged;
  }
  ...
}

IObservableVector extends various list interfaces (ILIst etc …) so we have to implement these on ObservableCollectionShim via straight-through adapter methods …

public int IndexOf(T item)
{
  return _adaptee.IndexOf(item);
}
 
public void Insert(int index, T item)
{
  _adaptee.Insert(index, item);
}
 
public void RemoveAt(int index)
{
  _adaptee.RemoveAt(index);
}
 
// etc ...

Now, the interesting part! The change notification is implemented by handling CollectionChanged events, and re-emitting them as VectorChanged events:

/// <summary>
/// Handles and adapts CollectionChanged events
/// </summary>
private void Adaptee_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
  VectorChangedEventArgs args = new VectorChangedEventArgs();
 
  switch (e.Action)
  {
    case NotifyCollectionChangedAction.Add:
      args.CollectionChange = CollectionChange.ItemInserted;
      args.Index = (uint)e.NewStartingIndex;
      break;
 
    case NotifyCollectionChangedAction.Remove:
      args.CollectionChange = CollectionChange.ItemRemoved;
      args.Index = (uint)e.OldStartingIndex;
      break;
 
    case NotifyCollectionChangedAction.Replace:
      args.CollectionChange = CollectionChange.ItemChanged;
      args.Index = (uint)e.NewStartingIndex;
      break;
 
    case NotifyCollectionChangedAction.Reset:
    case NotifyCollectionChangedAction.Move:
      args.CollectionChange = CollectionChange.Reset;
      break;
    }
  OnVectorChanged(args);
}

Note, there does not seem to be an equivalent for NotifyCollectionChangedAction.Move, however, I am pretty sure ObservableCollection never raises that change type anyway!

We can now make use of our ‘shim’ class within a view-model as follows:

private ObservableCollection<string> _items;
 
public IObservableVector<string> Items
{
  get
  {
    return  new ObservableCollectionShim<string>_items;
  }
}

However, if we were using this view-model within a cross-platform application, this would still cause problems, because IObservableCollection does not exist in Silveright / WPF. A more elegant solution is to wrap the ObservableCollection in the shim class within a value converter. Firstly, we need a way to create a shim without specifying the generic type argument (IValueConverter is not strongly typed, your bound values are presented as the type ‘object’):

public static class ExtensionMethods
{
  /// <summary>
  /// Creates an ObservableCollectionShim that adapts this ObservableCollection.
  /// </summary>
  public static object ToObservableVector(this INotifyCollectionChanged collection)
  {
    Type genericItemType = collection.GetType().GenericTypeArguments[0];
    Type shimType = typeof(ObservableCollectionShim<>);
    Type genericShimType = shimType.MakeGenericType(new Type[] { genericItemType });
    return Activator.CreateInstance(genericShimType, new object[] { collection });
  }
}

This can then be used within a value converter as follows:

public class ObservableCollectionAdapter : IValueConverter
{
  public object Convert(object value, string typeName, object parameter, string language)
  {
    return ((INotifyCollectionChanged)value).ToObservableVector();
  }
 
  public object ConvertBack(object value, string typeName, object parameter, string language)
  {
    throw new NotImplementedException();
  }
}

This allows us to adapt an ObservableCollection for use with WinRT simply by applying a value-converter within the View:

<ItemsControl ItemsSource="{Binding Path=MyCollection, Converter={StaticResource ObservableCollectionAdapter}}"/>

A nice side-effect of this approach is that if we replace the ObservableCollection within our view-model with a new instance, the binding framework will take care of creating a new ‘shim’ via the value converter.

This technique allows us to write cross-platform view-models using ObservableCollection for our collections of objects.

You can download the sourcecode with a simple example here: ObservableCollectionShim.zip

Regards, Colin E.

Developing Windows Phone 7 HTML5 apps with PhoneGap

September 29th, 2011

This article show the step-by-step development of a Windows Phone 7 HTML5 application using PhoneGap. It also looks at how viable this approach is for cross-platform mobile development.


Colin Eberhardt is a Scott Logic technical Evangelist and a Technical Architect for Visiblox, suppliers of high-performance WPF and Silverlight charts.

Introduction … and Why HTML5?

Windows Phone 7 allows native application development in both Silverlight and XNA, both of which are mature framework with excellent tool support. So why would you want to develop an application with HTML5 / JavaScript instead? Personally I think the only viable reason for doing this (other than just for fun!) is to develop a cross-platform mobile application. HTML5 / JavaScript applications are platform agnostics, running on Android, iPhone, BlackBerry and now with the Mango WP7 supporting IE9, WP7.
In this blog post I will describe how I implemented the application shown below, which allows you to search properties for sale, using the GPS and various webservices to find and geocode your current location:

The basic concept behind HTML5 applications is that your native application is simply a full-screen WebBrowser, which hosts your JavaScript application logic. In order to achieve this in practice, you need to package the HTML, JavaScript, CSS and other resources into a XAP file, then use the WebBrowser APIs to pass messages between your JavaScript code and the native APIs in order to access the phone features such as the camera, accelerometer etc …

A few people have developed frameworks to assist in development of HTML5 apps, for example the HTML App Host Framework. However, the one I would recommend is PhoneGap, as the name implies, this framework fills the gap between your JavaScript code and the native phone features. PhoneGap is an open source project that has been running for a couple of years now; currently a large range of phones are supported, meaning that you are guaranteed the same JavaScript APIs for accessing the phone features on Android, iPhone, BlackBerry and more.

PhoneGap support for Windows Phone 7 was initiated by Matt Lacey, who created an initial implementation of the PhoneGap APIs. More recently Nitobi (who run PhoneGap), announced an official beta release, which they have worked on in conjunction with Microsoft.

Getting Started With PhoneGap

PhoneGap has a project template which will help you create an initial “Hello World” style application. You can get started by following these steps:

  1.  Download the WP7 PhoneGap from https://github.com/phonegap/phonegap-wp7
  2.  Copy the file GapAppStarter.zip, which contains the project template, to the folder
    •  \My Documents\Visual Studio 2010\Templates\ProjectTemplates\
  3.  Create a new project using the GappAppStart project template. I could not locate this template within the tree of ‘Installed Templates’, so used the search function. See the image below.
  4.  Add the framework\WP7GapClassLib.csproj project to your solution and add a reference to this project.
  5. Build and run!

If you followed these steps correctly, you should see the following:

Dissecting the Example App

The example application is a bit more complex than an equivalent Silverlight ‘Hello World’ app, so we’ll look at it in some detail …

The www folder contains the HTML5 application sourcecode, here you place images, HTML, JavaScript, and CSS. These files are marked as ‘content’ and will be included within the XAP file with the same directory structure. The project contains a GapSourceDictionary.xml file, this XML file lists all the HTML application resources. When the application starts, this XML file is read, and each file is added to isolated storage so that it can be served by the WebBrowser control:

<GapSourceDictionary>
  <FilePath Value="www/index.html" /> 
  <FilePath Value="www/phonegap.1.0.js" /> 
  <FilePath Value="www/master.css" /> 
</GapSourceDictionary>

The MainPage.xaml, which is the Silverlight UI for the application contains an instance of PGView:

<phone:PhoneApplicationPage 
    ... xmlns:my="clr-namespace:WP7GapClassLib;assembly=WP7GapClassLib">
    <Grid>
      <my:PGView/>
    </Grid>
</phone:PhoneApplicationPage>

PGView is defined in the WP7GapClassLib, it is a UserControl which hosts a WebBrowser, and contains the code that bridges between the PhoneGap JavaScript APIs and the WP7 .NET APIs. On startup it will load your files into isolated storage and navigate the browser control to “www/index.html” (although this will probably be configurable in future).

The index.html file from the template project is shown below (minus a few un-important parts):

<!doctype html>
<html>
  <head>
    <meta name="viewport" content="width=320; user-scalable=no" />    
      <script type="text/javascript" charset="utf-8" src="phonegap.1.0.js"></script>       
      <script type="text/javascript">
        function init()
        {
          document.addEventListener("deviceready",onDeviceReady,false);
        }
 
        // once the device ready event fires, you can safely do your thing! -jm
        function onDeviceReady()
        {
          document.getElementById("welcomeMsg").innerHTML += "PhoneGap is ready!";
        }
      </script>
 </head>
 <body onLoad="init();">
  <h1>Hello PhoneGap</h1>
  <div id="welcomeMsg"></div>
 </body>
</html>

The important parts to note here are, firstly the viewport metadata, which relates to the complications that small mobile screens present to layout, resulting in the need for a separate layout viewport and visual viewport, as described in the article “a tale of two viewports“. Here the width is set to 320px, so each pixel-width in our HTML is two pixels on screen. I prefer one-to-one correlation, so would change this to the physical phone dimensions. The “user-scalable=no” should prevent the user from being able to pinch-zoom the page, unfortunately on my (mango) phone, this doesn’t seem to be working. This is a shame, because it really spoils the illusion of this being an application rather than a web-page.

When the DOM is loaded, the init function is invoked, which adds a handler for the deviceready event. When this has been fired, it is safe to use the PhoneGap APIs. This example does not actually use any of the APIs, but you can make a simple change to get the phone name via device.name for example to verify that PhoneGap really is working.
So how does the JavaScript / Silverlight bridge work? The PhoneGap.js file communicates via window.external.Notify, which raises the ScriptNotify event within the WebBrowser control. Encoded strings are sent to the PGView control which uses the command pattern to identify the class which performs the required function, and executes it.

Creating the Property Search Application

Following the principles of Unobtrusive JavaScript, where presentation is separated from logic, the I have moved application logic into a file propertySearch.js (adding this to GapSourceDictionary.xml of course). The HTML has the following markup, a simple form that can be used to input the string to search for:

<h1 id="welcomeMsg">Find A Home</h1>
<form id="form" action="#">
  <input type="text" id="searchText"/>
  <input type="submit" id="searchButton" value="Go" />
  <input type="button" id="getLocationButton" value="My Location"/>
</form>

The event handlers are wired up as follows:

$(document).ready(function () {
  document.addEventListener("deviceready", onDeviceReady, false);
});
 
// phonegap is initialised
function onDeviceReady() {
  // verify that the phone is ready!
  $("#welcomeMsg").append("...");
 
  $("#form").submit(searchProperties);
  $("#getLocationButton").click(getGeolocation);
}

Note, I have also added jQuery to the project. The code which appends an ellipsis (…) to the title is a subtle indication that the deviceready event has fired. I found when using the emulator that often the JavaScript code within the page was not being executed at all, this seems to have nothing to do with the PhoneGap JavaScript code. Fortunately the code is always executed when deployed to a real device.

The search properties function is a simple AJAX call to the Nestoria APIs, using the jQuery ajax() function, which manages the JSONP process of dynamically adding a script tag to the DOM which fetches the required data.

var propertyTemplate = $("#propertyTemplate").template();
 
// searches for properties based on the current search text
function searchProperties() {      
  var query = "http://api.nestoria.co.uk/api?" +
                  "country=uk&pretty=1&action=search_listings&encoding=json&listing_type=buy" +
                  "&place_name=" + $("#searchText").val();
  $.ajax({
    dataType: "jsonp",
    url: query,
    success: function (result) {
 
      var resultsContainer = $("#resultsContainer");
      resultsContainer.empty();
      $.each(result.response.listings, function (index, property) {
        var $itemNode = $.tmpl(propertyTemplate, property).data("propertyData", property);
        $itemNode.appendTo(resultsContainer);
      });
    }
  });
 
  return false;
}

NOTE: The application uses the Nestoria UK APIs, so if you are trying it out in your emulator, set your location to somewhere in the UK!

I am using a jQuery templates to render each item. You can render an array of items via the tmpl() function, however, here I render each one individually, so that I can associate the raw JSON data for each property with the generated element via the jQuery data() function. This is rather like defining the DataContext for a Silverlight FrameworkElement.

<script id="propertyTemplate" type="text/x-jquery-tmpl">
  <li class="property" onClick="propertyClick(this); return false;">
    <div class="propertyContainer">
      <div class="thumbnailColumn">
        <img src="${thumb_url}" class="thumbnail"/>
      </div>
      <div class="detailsColumn">
        <div class="title">${title}</div>
        <div class="price">${price_formatted}</div>
      </div>
    </div>
  </li>
</script>

When a property is clicked, the following function is invoked, which locates the clicked DOM element, then extract the JSON property details via the data() function described above. This is used in conjunction with another template to render the property details:

// handle clicks on properties
function propertyClick(propertyNode) {
 
  // get the property
  var property = $(propertyNode).data("propertyData");
 
  // render the template
  $("#detailsContainer").empty();
  $.tmpl(propertyDetailsTemplate, property).appendTo("#detailsContainer");
 
  // switch pages
  showDetailsPage();
}

The template is shown here:

<script id="propertyDetailsTemplate" type="text/x-jquery-tmpl">
  <div>
    <h1>${price_formatted}</h1>
    <img src="${img_url}" class="propertyImage"/>			  
    <h2>${title}</h2>
    <div class="bedrooms">bedrooms: ${bedroom_number}</div>
    <div class="bathrooms">bathrooms: ${bathroom_number}</div>
    <div class="summary">${summary}</div>
  </div>
</script>

The navigation between pages is achieved by having the markup for both pages present in the DOM:

<body>
  <div class="page searchPage">
     ...
  </div>
  <div class="page detailsPage">
    ...
  </div>
</body>

The pages are styled to use absolute positioning:

div.page 
{
  position: absolute;
  width: 480px;  
}

We can then show / hide these pages by simply animating their CSS left property:

function showSearchPage() {
  $(".detailsPage").animate({ left: 480 }, 300, function () {
    $(this).hide();
  });
  $(".searchPage").show().animate({ left: 0 }, 300);
};
 
function showDetailsPage() {
  $(".detailsPage").show().animate({ left: 0 }, 300);
  $(".searchPage").animate({ left: -480 }, 300, function () {
    $(this).hide();
  });
};

It is worth noting that if the IE9 browser supported CSS3 transitions all of the above could have been done declaratively within CSS, unfortunately animations are not in the list of supported features. There is nothing wrong with jQuery animations, they have a very concise and simple syntax. However, CSS3 animation give the browser the option to use GPU acceleration, greatly improving performance. The webkit browsers on Android and iOS support this feature (using the –webkit prefix on the required CSS properties).

The current location is identified via navigator.geolocation.getCurrentPosition, using the Bing Maps REST APIs to geocode from a lat / long coordinate to a postcode:

// gets the current phone location
function getGeolocation() {
  navigator.geolocation.getCurrentPosition(function (position) {
    // geocode via Bing Maps
    var apiKey = "Ai9-KNy6Al-r_ueyLuLXFYB_GlPl-c-_iYtu16byW86qBx9uGbsdJpwvrP4ZUdgD";
    var query = "http://dev.virtualearth.net/REST/v1/Locations/" + position.coords.latitude +
                    "," + position.coords.longitude + "?jsonp=onGeocode&key=" + apiKey
 
    $.ajax({
      dataType: "jsonp",
      url: query
    });
  });
}
 
// handle the geocode results
function onGeocode(result) {
  // extract the 'outward' part of the postcode
  var postalCode = result.resourceSets[0].resources[0].address.postalCode;
  var codeSplit = postalCode.split(" ");
  if (codeSplit.length > 0) {
    $("#searchText").val(codeSplit[0]);
  }
}

Metro-style

The standard style for HTML controls doesn’t look that great, however, with a simple bit of CSS it is possible to re-create something similar to the WP7 Metro styles. Here I have copied some of the properties that are present in the Silverlight resource dictionaries:

body, input, div
{
  font-size: 22.667px; /* PhoneFontSizeMedium */
}
 
h2
{
  font-weight: normal;
  font-size: 32px; /* PhoneFontSizeLarge */
}
 
h1
{
  font-weight: normal;
  font-size: 42.667px; /* PhoneFontSizeExtraLarge */
}
 
input[type="button"], input[type="submit"]
{
  background: black;
  color: white;
  border-color: white;
  border-style: solid;
  padding: 4px 10px;
  border-width: 3px; /* PhoneBorderThickness */
  font-size: 25.333px; /* PhoneFontSizeMediumLarge */
}
 
input[type="button"]:active, input[type="submit"]:active
{
  background: white;
  color: black;
}
 
input[type="text"]
{
  width: 150px;
  padding: 4px;
}

ProgressBar

For a bit of fun I thought I’d try and create a HTML equivalent of the ‘trailing dots’ WP7 ProgressBar. With a simple bit of markup / CSS:

<div class="progress">
  <div class="pip"></div>
  <div class="pip"></div>
  <div class="pip"></div>
  <div class="pip"></div>
  <div class="pip"></div>
</div>
.progress div
{
  width: 5px;
  height: 5px;
  overflow: hidden;
  position: absolute;  
  background: green;
}
 
.progress
{
  position: relative;
  height:10px;
}

And some further jQuery animations, this time making use of the easing plugin:

function onDeviceReady() {
  ...
 
  // create an animation loop for the progress bar;
  startAnimation();
  var tid = setInterval(startAnimation, 3500);
}
 
function startAnimation() {
  var delay = 200;
  $(".pip").each(function () {
    animatePip($(this), delay);
    delay += 200;
  });
}
 
function animatePip(element, delay) {
  element.css("left", 0)
        .hide()
        .delay(delay)
        .show()
        .animate({ left: 240 }, { duration: 1000, easing: "easeOutSine" })
        .animate({ left: 480 }, { duration: 1000, easing: "easeInSine" });
}

We get something which approximates the WP7 progress bar:

(Note, sometimes the animation goes a bit crazy, try refreshing your browser to reset it!)

Again, my comments regarding CSS3 animations and GPU acceleration apply here also.

Tombstoning

As I have discussed in a previous blog post, tombstoning is a tricky task for WP7 developers. When your application is tombstoned you can save application state, page state – and the framework stores your back-stack URIs. It is your responsibility to re-start your application in the same state. Contrary to the belief of some developers I have talked to, Mango does not remove the need to tombstone, it just means that your application is likely to be tombstoned less often, with the suspended state being used to park your application while application switching.

So, how does tombstoning work with a PhoneGap HTML5 application? Good question! Implanting tombstoning would probably require some custom communication, outside of the PhoneGap APIs, that allows the JavaScript application to provide its current state to the Silverlight application that hosts this.

I would be very interested to hear from anyone who has solved this issue!

The Development Process

Attempting to develop JavaScript applications with the browser within the WP7 emulator is not a pleasant process. Any JavaScript errors, including simple parsing errors, typically result in the HTML being rendered but the scripts ignored. A much better approach is to run your HTML / JavaScript within a browser, this gives you access to the usual developer tools such as Firebug, or the built in Chrome / IE tools.

However, to do this, you need to mock the PhoneGap APIs. For my application, I found the following did the trick:

document.addEventListener = function (evt, handler, capture) {
  $("body").bind(evt, handler);
};
 
$(document).ready(function () {
  setTimeout(function () {
    $("body").trigger("deviceready");
  }, 100);
});

OK, I know what you are thinking, what about the geolocation code? Where is the mock for these PhoneGap APIs? The answer is that a number of the PhoneGap APIs are designed to exactly match the corresponding HTML5 specification. So for example, the PhoneGap geolocation API is exactly the same as the HTML5 geolocation APIs. For phones such as WP7 where the browser supports this HTML5 feature, PhoneGap does nothing, for phones that do not, PhoneGap provides an implementation (using the native APIs).

If the PhoneGap mock is so simple, you might be wondering, why use PhoneGap at all? Well, PhoenGap is still giving us a mechanism for packaging files into a XAP file in such a way that they can be rendered by the browser.

How cross-platform is this approach?

I would say that this approach is probably about as cross-platform as any HTML5 / JavaScript browser based application. There are always going to be cross-browser differences to overcome. As a test, I ran this code on an iPod Touch without any modification. The results are pretty good, although there are some quirks visible:

How viable are HTML5 applications?

It is clear that there is a growing trend towards cross-platform HTML5 applications, and Microsoft seem to be in support of this concept (even though the JavaScript / HTML5 Metro applications within Win8 are not cross-platform!). Microsoft has worked with Nitobi to create the PhoneGap for WP7, it was also announced at the //build/ conference that Microsoft would be working with jQuery Mobile to create a Metro theme for their mobile controls.

Currently, PhoneGap for WP7 is in beta, and it is certainly a little rough around the edges. This will no doubt improve in time. The large list of applications written with PhoneGap, certainly indicate that this is a viable solution for application development.

A larger obstacle for WP7 HTML5 applications is the IE9 browser that runs in the Mango phone. Whilst it has a pretty impressive list of HTML5 features supported, there are a couple of browser features / issues that I cannot resolve. These make it obvious to any user that this is in fact a browser application, totally spoiling the ‘illusion’

  1. user-scalable=no – currently this setting seems to be ignored. This means that the user can pinch your application, which makes it feel like a web page. UPDATE Roy, in a comment below pointed out that the viewport parameters should be comma-separated. This almost solves the issue. The user can still pinch the view, which causes it to zoom, but when the pinch finishes, the viewport returns to the original scale. Not perfect, but better than nothing!
  2. There doesn’t seem to be any way to turn of the gray shaded rectangle that appears over links when you click them. With applications that have dynamic content, this can look pretty ridiculous, with a gray rectangle lingering on screen while the page content changes underneath it. With the Android / iPhone browsers this can be turned off in CSS via -webkit-tap-highlight-color

Hopefully these limitations will be resolved. When they do, I am quite sure that HTML5 will be a viable technology for creating quality phone applications.

You can download the project sourcecode here: PhoneGapExample.zip

Regards, Colin E.

XAML Finance – a cross-platform WPF, Silverlight, WP7 application

September 19th, 2011

I have just published a new article on codeproject which describes the development of XAMLFinance, a cross-platform application for the desktop (WPF), web (Silverlight) and phone (WP7).


Head over to codeproject to read about the development of this application and download the sourcecode.


Regards, Colin E.

TweetSearch – A Cross platform Metro UI WinRT and Silverlight Application

September 16th, 2011

With the Windows 8 preview release earlier this week, developers are now faced with a whole new and exciting Microsoft stack. The Windows 8 architecture has something of a split-personality, incorporating a completely new runtime, WinRT together with the older Win32 and .NET framework. However, these sit on different sides of a physical divide in both the architecture and the user interface. For more details of this, see my earlier article.

The new WinRT APIs support both C# and XAML, having an interface that is very similar to its .NET counterparts. However, there are a number of differences, most importantly much of the UI now resides in a new namespace. It is clear that transitioning from Silverlight, WP7 or WPF development to using WinRT will be quite easy.

I am sure there exists classes of application that sit best in the Win8 Metro UI, for which you will use WinRT for development. There will also be classes of application that sit best in the Win8 classic desktop, for which you will still be using WPF and Silverlight to develop.

However … there will certainly exist a class of application that sits well in both the Metro and Desktop environment. In the Win8 preview IE does just this.

This introduces a problem, how can we share code between our Silverlight Desktop and WinRT Metro applications?

I have previously presented talks and written articles on cross-platform application development. So I thought I would grab myself a copy of the Win8 preview release and see how easy it is to code-share between a Silverlight and WinRT Metro application.

This blog post describes a simple TwitterSearch application, the Silverlight version is shown below. Simply type in a search string to see the results retrieved from twitter:

A screenshot of the TwitterSearch application built for WinRT Metro is shown below:

When code-sharing between WPF, Silverlight and WP7 applications separation of concerns and solid design patterns are essential in order to navigate the framework differences. For that reason I use the Model-View-ViewModel pattern, this makes it easy to specialise the view for each platform, where they typically differ the most. I will use this same approach for WinRT.

The code is shared between the Silverlight and Win8 Metro application by having two separate solutions which both share files via the Visual Studio file linking feature.

The TwitterSearchViewModel which backs this application simply exposes a search string property and a list of tweets:

public partial class TwitterSearchViewModel : INotifyPropertyChanged
{
  private TweetViewModelCollection _tweets = new TweetViewModelCollection();
 
  private IMarshalInvoke _marshalInvoke;
 
  private string _searchText = "WinRT";
 
  public TwitterSearchViewModel(IMarshalInvoke marshalInvoke)
  {
    _marshalInvoke = marshalInvoke;
  }
 
  /// <summary>
  /// Gets / sets the search term
  /// </summary>
  public string SearchText
  {
    get {  return _searchText;  }
    set
    {
      _searchText = value;
      OnPropertyChanged("SearchText");
    }
  }
 
  /// <summary>
  /// Gets the search results
  /// </summary>
  public TweetViewModelCollection Tweets
  {
    get { return _tweets; }
    set { _tweets = value; }
  }
 
  /// <summary>
  /// Gets a command that executes the search
  /// </summary>
  public ICommand ExecuteSearchCommand
  {
    get
    {
      return new DelegateCommand(() => ExecuteSearch());
    }
  }
}

The IMarshalInvoke interface allows the view model to perform asynchronous calls via the Dispatcher, without being dependant on this UI class, more on this later.

It is here that I hit the first problem, WinRT has ICommand, but it lives in a different namespace! Time to add a WinRT compilation symbol to my WinRT project and throw in a pre-compiler directive:

#if WINRT
 
using Windows.UI.Xaml.Input;
 
#else
 
using System.Windows.Input;
 
#endif

Now my view model compiles just fine. However, when I hooked it up with the view, my bindings were only working in one direction. After appealing to Twitter, the MSDN forums and the WPF Disciples, Laurent Bugnion kindly informed me that WinRT has two different INotifyPropertyChanged interfaces. One within System.ComponentModel and the other in Windows.UI.Xaml.Data, if you use the one from System.ComponentModel it is ignored by the WinRT binding framework. Very confusing!

Fixing this is simply a matter of adding more platform specific namespaces:

#if WINRT
 
// used for ICommand
using Windows.UI.Xaml.Input;
 
// used for INotifyPropertyChanged
using Windows.UI.Xaml.Data;
 
#else
 
using System.ComponentModel;
using System.Windows.Input;
 
#endif

My original code used WebClient, which is not present in WinRT. Fortunately HttpWebRequest is present in both, so I changed my code to use that, hence the need for the IMarshalInvoke interface to marshal execution back onto the UI thread.

As a result, much of the core logic, which makes use of Linq-to-XML, in the view model is shared between both applications:

/// <summary>
/// Parses the response from our twitter request, creating a list of TweetViewModel instances
/// </summary>
private void ParseXMLResponse(string xml)
{
  var doc = XDocument.Parse(xml);
  var items = doc.Descendants(AtomConst.Entry)
                  .Select(entryElement => new TweetViewModel()
                  {
                    Title = entryElement.Descendants(AtomConst.Title).Single().Value,
                    Id = long.Parse(entryElement.Descendants(AtomConst.ID).Single().Value.Split(':')[2]),
                    ProfileImageUrl =  entryElement.Descendants(AtomConst.Link).Skip(1).First().Attribute("href").Value,
                    Timestamp = DateTime.Parse(entryElement.Descendants(AtomConst.Published).Single().Value),
                    Author = entryElement.Descendants(AtomConst.Name).Single().Value
                  });
 
  _tweets.Clear();
  foreach (var item in items)
  {
    _tweets.Add(item);
  }
}
 
/// <summary>
/// Searches for the given term
/// </summary>
private void ExecuteSearch()
{
  IsSearching = true;
 
  // perform the search
  string uri = _twitterUrl + SearchText;
  HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(uri));
  request.BeginGetResponse(new AsyncCallback(ReadCallback), request);
}
 
/// <summary>
/// A callback that receives the search results
/// </summary>
private void ReadCallback(IAsyncResult asynchronousResult)
{
  HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
  HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
  using (StreamReader streamReader1 = new StreamReader(response.GetResponseStream()))
  {
    string resultString = streamReader1.ReadToEnd();
 
    // marshall onto the UI thread and parse
    _marshalInvoke.Invoke(() =>
      {
        Tweets.Clear();
        ParseXMLResponse(resultString);
        IsSearching = false;
      });
  }
}

This makes me happy :-)

On a side note, whilst WinRT does not have WebClient, it actually has something much better via classes such as SyndicationFeed which make use of the new C# async and await pattern for dealing with asynchrony. Of course, using these new APIs reduces the amount of code you share with the Silverlight .NET code.

The DelegateCommand implementation again hits the same problem of the same interface being located in a different namespace, yet despite this, most of the code is shared:

#if WINRT
 
// used for ICommand
using Windows.UI.Xaml.Input;
 
#else
 
using System.Windows.Input;
 
#endif
 
namespace SLUGUK.ViewModel
{
  public class DelegateCommand : ICommand
  {
    private Action _action;
 
    public DelegateCommand(Action action)
    {
      _action = action;
    }
 
    public bool CanExecute(object parameter)
    {
      return true;
    }
 
#if WINRT
    public event Windows.UI.Xaml.EventHandler CanExecuteChanged;
#else
    public event EventHandler CanExecuteChanged;
#endif
 
    public void Execute(object parameter)
    {
      _action();
    }
  }
}

Despite the various XAML UI controls being in a different C# namespace, they use the same URIs for their XML namespace mappings. This makes it possible to share XAML as well as C# code between your applications.

However, in much the same way as it makes little sense to share XAML between WP7 and Silverlight due to differences in form-factor and user-experience, I don’t think it makes sense to share XAML between Silverlight and Metro WinRT. The primary focus of the Win8 Metro applications is the touch-first tablet form factor, hence you would expect it to be quite different from a desktop application.

The following is the XAML for my Metro UI application:

<UserControl x:Class="WinRTMetroTwitter.View.TwitterSearchView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="using:WinRTMetroTwitter.View"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
 
    <UserControl.Resources>
        <local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
        <local:ImageConverter x:Key="ImageConverter"/>        
    </UserControl.Resources>
 
    <Grid x:Name="LayoutRoot" Background="#FF0C0C0C">
        <Grid Margin="50"
              Width="400"
              HorizontalAlignment="Left">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal">
                <TextBox Text="{Binding Path=SearchText, Mode=TwoWay}"
                     Width="200"/>
                <Button Command="{Binding Path=ExecuteSearchCommand}"
                    Content="Go!"/>
                <TextBlock VerticalAlignment="Center"
                     Visibility="{Binding Path=IsSearching, Converter={StaticResource BoolToVisibilityConverter}}"
                     Text="Searching ..."
                     Margin="20,0,0,0"
                     FontSize="15"/>
            </StackPanel>
 
            <!-- renders the search results -->
            <ItemsControl x:Name="itemsControl"
                          Grid.Row="2">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Vertical"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal"
                                    Margin="5">
                           <Image Source="{Binding ProfileImageUrl, Converter={StaticResource ImageConverter}}"
                                  Width="96" Height="96"
                                  Stretch="UniformToFill"
                                  Margin="2"
                                  VerticalAlignment="Top"/>
 
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="{Binding Title}"
                                           TextWrapping="Wrap"
                                           FontSize="15"
                                           Width="300"
                                           HorizontalAlignment="Left"/>
                                <StackPanel Orientation="Horizontal">
 
                                    <TextBlock FontWeight="Bold"
                                               Text="{Binding Path=Author}"
                                               FontSize="15"/>
 
                                    <TextBlock Margin="10,0,0,0"
                                              Text="{Binding Path=Timestamp}"
                                               FontSize="15"
                                              Foreground="#333"/>
                                </StackPanel>
                            </StackPanel>
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
 
            <Rectangle Fill="#FF0C0C0C"
                   Opacity="0.5"
                   Grid.Row="1"
                   Visibility="{Binding Path=IsSearching, Converter={StaticResource BoolToVisibilityConverter}}"/>
        </Grid>
    </Grid>
</UserControl>

If you are a Silverlight developer you will notice that everything here is standard Silverlight, it is just wired-up to different classes via the XAML namespace mappings.

I did discover that the IValueConverter signature within WinRT is a bit different. Within this application, the value conversion logic is so simple it is hardly worth code-sharing, however, it would be a very simple exercise if you wished to do so.

namespace WinRTMetroTwitter.View
{
  public class BoolToVisibilityConverter : IValueConverter
  {
    public object Convert(object value, string typeName, object parameter, string language)
    {
      return (bool)value ? Visibility.Visible : Visibility.Collapsed;
    }
 
    public object ConvertBack(object value, string typeName, object parameter, string language)
    {
      throw new NotImplementedException();
    }
  }
}

Finally, I did hit against one further problem. With WinRT it seems that there is a currently a bug which prevents ItemsControl binding to collections via INotifyCollectionChanged from working. This has been raised in the MSDN forums and I am sure it will be fixed. As a simple workaround I added the following code-behind (gasp!):

public sealed partial class TwitterSearchView : UserControl
{
  private TwitterSearchViewModel feedViewModel;
 
  public TwitterSearchView()
  {
    InitializeComponent();
 
    this.Loaded += ResultsView_Loaded;
  }
 
  private void ResultsView_Loaded(object sender, RoutedEventArgs e)
  { 
    feedViewModel = this.DataContext as TwitterSearchViewModel;
 
    // detect collection changed
    feedViewModel.Tweets.CollectionChanged += FeedItems_CollectionChanged;
  }
 
 
  private void FeedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  {
    // manually 'refresh' the UI
    itemsControl.ItemsSource = null;
    itemsControl.ItemsSource = feedViewModel.Tweets;
  }
}

Conclusions

Creating a cross-platform WinRT / Silverlight application is entirely possible, using the same techniques that we currently employ for cross-platform Silverlight / WPF / WP7 applications. There are further complications relating to classes living in different namespaces and there will of course be other detail-level differences that this simple example does not touch upon. However, I think this presents a feasible and promising route for re-using existing Silverlight/ WPF code, and for developing applications that target the Win8 tablet, WP7 phone and Silverlight (or WPF) desktop with a single code-base.

You can download the full sourcecode: CrossPlatformTwitter.zip

Regards, Colin E.