Colin Eberhardt's Adventures in .NET

Handling the back-stack in Windows Phone 7 PhoneGap applications

November 28th, 2011

Recently I have been researching the use of PhoneGap for creating HTML5 Windows Phone 7 applications. I have written an introductory post on the subject and also managed to have a HTML5-based application accepted into the marketplace.

The Windows Phone 7 execution model has a few unique features (when compared to Android and iOS) that need to be considered when developing HTML5 applications. The first one, tombstoning, is something I dealt with in an earlier blogpost. In this post I want to look at how to handle the back-button and the application back-stack.

With Windows Phone 7, as the user navigates from one page to the next, the latest page is added to the top of a stack (the back-stack). The back-button is used to navigate back through the pages within your application by pulling them from the top of this stack. When the back stack holds a single page, i.e. the initial entry point into your application, pressing the back-button should exit the application.

So, how do you support this with a HTML5 / PhoneGap application? The PhoneGap APIs expose the back-button as a JavaScript event. If you subscribe to this event, your JavaScript code is notified each time the user hits the back button. Also, if the JavaScript event is subscribed to, the ‘native’ (C#) event is cancelled, which means application back-stack remains unchanged and your PhoneGap application does not exit.

In order to emulate the back-stack functionality in a PhoneGap application you need to subscribe to the PhoneGap backbutton when back-navigation is possible within your application, and unsubscribe when it is not. This means that when the user hits the back-button at the entry-point of your application, the application is terminated as expected.

A JavaScript Back-Stack

In order to handle the PhoenGap backbutton event, I felt the easiest approach would be to structure my JavaScript application so that it more closely resembles a Windows Phone 7 application. I have already discussed in my blog post on tombstoning how this task was made easier by using KnockoutJS to structure my code using the Model-View-ViewModel (MVVM) pattern.

In this blog I’ll add further structure to the code, starting with an ApplicationViewModel. This view model contains a stack of view-models, with the one that is at the top of the stack, exposed via the currentViewModel observable property, being the one which is rendered on screen. The ApplicationViewModel also exposes functions for adding to and removing items from this stack:

function ApplicationViewModel() {
 
  var that = this;
 
  this.viewModelBackStack = ko.observableArray();
 
  this.backButtonRequired = ko.dependentObservable(function () {    
    return this.viewModelBackStack().length > 1;
  }, this);
 
  this.currentViewModel = ko.dependentObservable(function () {
    return this.viewModelBackStack()[this.viewModelBackStack().length-1];
  }, this);
 
  this.navigateTo = function (viewModel) {
    this.viewModelBackStack.push(viewModel);
  }
 
  this.back = function () {
    this.viewModelBackStack.pop();
   }
}

The currentViewModel is implemented as a dependent-observable, a really neat KnockoutJS feature where you create a new observable property based on one or more observable properties of a view model. In this case, KnockoutJS does some magic behind the scenes to deduce that
currentViewModel depends upon the viewModelBackStack observable array. Each time the array is modified, bindings that depend on currentViewModel are also updated. That’s pretty neat!

The UI code needs to be modified so that the view relating to the currentViewModel is rendered and bound to its respective view model. This is simply a matter of subscribing to changes in this observable property in our code:

// create the view model
application = new ApplicationViewModel();
 
application.currentViewModel.subscribe(function (viewModel) {
  if (viewModel !== undefined) {
    $("#app").empty();
    $("#" + viewModel.template).tmpl("").appendTo("#app");
    ko.applyBindings(viewModel);
  }
});
 
application.navigateTo(new TwitterSearchViewModel());

For the above code to work, each view model must expose a template property which identified the HTML template which is their corresponding view. The above code creates a view instance and appends it to the #app element.

The templates for the two view-models within this application are shown below:

<script type=text/x-jquery-tmpl" charset="utf-8" id="twitterSearchView">
  <div>
    <form data-bind="submit: search">
        <input data-bind="value: searchTerm, valueUpdate: 'afterkeydown'" />
        <button type="submit" data-bind="enable: searchTerm().length > 0 && isSearching() == false">Go</button>  
    </form>
    <ul data-bind="template: {name: 'tweetView', foreach: tweets}"> </ul>
  </div>
</script>
 
<script type="text/x-jquery-tmpl" charset="utf-8" id="tweetView">
  <li class="tweet"
    data-bind="click: select">
    <div class="thumbnailColumn">
      <img data-bind="attr: { src: thumbnail }" class="thumbnail"/>
    </div>
    <div class="detailsColumn">
      <div class="author" data-bind="text: author"/> 
      <div class="text" data-bind="text: text"/> 
      <div class="time" data-bind="text: time"/> 
    </div>
  </li>
</script>
 
<script type="text/x-jquery-tmpl" charset="utf-8" id="tweetDetailView">
  <div class="tweet">
    <div class="thumbnailColumn">
      <img data-bind="attr: { src: thumbnail }" class="thumbnail"/>
    </div>
    <div class="detailsColumn">
      <div class="author" data-bind="text: author"/> 
      <div class="text" data-bind="text: text"/> 
      <div class="time" data-bind="text: time"/> 
    </div>
  </div>
</script>
 
<h1 id="welcomeMsg">Twitter Search</h1>   
<div id="app" />

When a tweet is clicked on, the select function is invoked via the Knockout binding. This simply pushes a TweetViewModel instance onto the application stack:

this.select = function () {
  application.navigateTo(this);
};

Handling the Back-Button

The above code navigates to the given view model, this will cause the currentViewModel dependent observable to notify that a change has occurred and the view that relates to this view model will be rendered. The next step is to wire up the back-button.

The ApplicationViewModel has a boolean backButtonRequired dependent observable that indicates whether the JavaScript code needs to handle the back-button. This is true whenever there is more than one item in the back-stack:

this.backButtonRequired = ko.dependentObservable(function () {    
  return this.viewModelBackStack().length > 1;
}, this);

When the application is initially created, we handle changes to the observable as follows:

application.backButtonRequired.subscribe(function (backButtonRequired) {
  if (backButtonRequired) {
    document.addEventListener("backbutton", onBackButton, false);
  } else {
    document.removeEventListener("backbutton", onBackButton, false);
  }
});
 
function onBackButton() {
  application.back();
}

And that’s it! When the JavaScript back-stack has more than one item, the PhoneGap backbutton event is handled and the back function invoked on our application, popping the top item from the stack, with the view updating accordingly. When our JavaScript back-stack has a single item, we no longer handle the PhoneGap backbutton event, this results in the Silverlight container handling the back-button. The Silverlight application back-stack always has a single page, so our application will exit.

This technique for handling the back-stack is simple and elegant, this example has just two simple pages, however this same pattern should scale well for more complex HTML5 applications.

You can download the full sourcecode here: PhoneGapBackStack.zip

Regards, Colin E.

Pushpin Clustering with the Windows Phone 7 Bing Map control

November 21st, 2011

This blog post provides a simple utility class that will cluster pushpins on a Bing Map control. This utility provides a way to achieve great performance with 1000s of pushpins.

The Bing Map control for Windows Phone 7 is a versatile control, allowing you to provide your users with an interactive map. The control has some very useful features such as Pushpins, which you can anchor to a map coordinate so that they move automatically as the user pans / zooms the map. However, I have found that in practice, if you have more than ~30 pushpins visible on a map, the pan / zoom performance starts to degrade (on a real device). Therefore, in order to provide the best user-experience, it is advisable to only render a handful of pushpins on the map at any one time.

This blog post describes a simple approach to clustering pushpins, as shown in the video below, which renders the location of ~500 juggling clubs worldwide:

The clustering code is very easy to use; just create a PushpinClusterer instance, pass your Pushpins, the map and the template to use for the clusters marker:

var clusterer = new PushpinClusterer(map, pins,
        this.Resources["ClusterTemplate"] as DataTemplate);

And that’s it!

The Implementation

Clustering of map markers is a common problem. Searching the internet I found a number of blog posts describing techniques for clustering Google Maps markers, one such blog post described a simple algorithm which clusters points that are within a fixed pixel distance from each other. I decided to take this algorithm and apply it to the Silverlight Bing Maps control.

The clusterer is a pretty simple class, whenever the map view changes (which occurs after pan or zoom), the clusterer iterates over all the pins, any that are closer than 50 pixels to each other, are merged together.

Once the pins have been clustered, only those which would currently be visible (based on the map viewport) are added to the map:

/// <summary>
/// Clusters the given pins on the supplied map
/// </summary>
public PushpinClusterer(Map map, List<Pushpin> pins, DataTemplate clusterTemplate)
{
  _map = map;
  _pins = pins;
  ClusterTemplate = clusterTemplate;
 
  _map.ViewChangeEnd += (s, e) => RenderPins();
}
 
 
/// <summary>
/// Re-render the pushpins based on the current zoom level
/// </summary>
private void RenderPins()
{
  List<PushpinContainer> pinsToAdd = new List<PushpinContainer>();
 
  // consider each pin in turn
  foreach (var pin in _pins)
  {
    var newPinContainer = new PushpinContainer(pin,
      _map.LocationToViewportPoint(pin.Location));
 
    bool addNewPin = true;
 
    // determine how close they are to existing pins
    foreach(var pinContainer in pinsToAdd)
    {
      double distance = ComputeDistance(pinContainer.ScreenLocation, newPinContainer.ScreenLocation);
 
      // if the distance threshold is exceeded, do not add this pin, instead
      // add it to a cluster
      if (distance < DistanceThreshold)
      {
        pinContainer.Merge(newPinContainer);            
        addNewPin = false;
        break;
      }
    }
 
    if (addNewPin)
    {
      pinsToAdd.Add(newPinContainer);
    }
  }
 
  // asynchronously update the map
  _map.Dispatcher.BeginInvoke(() =>
    {
      _map.Children.Clear();
      foreach (var projectedPin in pinsToAdd.Where(pin => PointIsVisibleInMap(pin.ScreenLocation, _map)))
      {
        _map.Children.Add(projectedPin.GetElement(ClusterTemplate));
      }
    });
 
}
 
/// <summary>
/// Gets whether the given point is within the map bounds
/// </summary>
private static bool PointIsVisibleInMap(Point point, Map map)
{
  return point.X > 0 && point.X < map.ActualWidth &&
          point.Y > 0 && point.Y < map.ActualHeight;
}
 
/// <summary>
/// Computes the cartesian distance between points
/// </summary>
private double ComputeDistance(Point p1, Point p2)
{
  return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}

The PushpinContainer is a class that holds a Pushpin, however, if one or more additional Pushpins are added to it, it will become a cluster. The class is given in full below:

/// <summary>
/// A container for one or more pushpins at a given screen coordinate.
/// </summary>
public class PushpinContainer
{
  private List<Pushpin> _pushpins = new List<Pushpin>();
 
  /// <summary>
  /// Creates a container for the given pushpin
  /// </summary>
  public PushpinContainer(Pushpin pushpin, Point location)
  {
    _pushpins.Add(pushpin);
    ScreenLocation = location;
  }
 
  /// <summary>
  /// Adds the pins from the given container
  /// </summary>
  public void Merge(PushpinContainer pinContainer)
  {
    foreach (var pin in pinContainer._pushpins)
    {
      _pushpins.Add(pin);
    }
  }
 
  /// <summary>
  /// Gets or sets the current screen location of this container
  /// </summary>
  public Point ScreenLocation { get; private set; }
 
  /// <summary>
  /// Gets the visual representation of the contents of this container. If it is 
  /// a single pushpin, the pushpin itself is returned. If multiple pushpins are present
  /// a pushpin with the given clusterTemplate is returned.
  /// </summary>
  public FrameworkElement GetElement(DataTemplate clusterTemplate)
  {
    if (_pushpins.Count == 1)
    {
      return _pushpins[0];
    }
    else
    {
      return new Pushpin()
      {
        Location = _pushpins.First().Location,
        Content = _pushpins.Select(pin => pin.DataContext).ToList(),
        ContentTemplate = clusterTemplate,
        Background = new SolidColorBrush(Colors.Red)
      };
    }
  }    
}

The GetElement method will either return the Pushpin, if it has not been clustered, or a new Pushpin with the ClusterTemplate applied. Note, the Content property of the clustered pin is a list of the DataContext properties of all the pins it ‘contains’.

The example application, which renders the location of ~500 juggling clubs, uses a very simple cluster template which simply indicates the number of points that have been clustered:

<DataTemplate x:Key="ClusterTemplate">
  <TextBlock Text="{Binding Count}"/>
</DataTemplate>

However, I am sure that with a bit of creativity, a more interesting template could be created!

Finally, the example application handles left mouse-clicks on the map, inspecting the DataContext of the clicked element in order to render the juggling club or cluster of juggling clubs which were clicked upon:

private void Map_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  var fe = e.OriginalSource as FrameworkElement;
  if (fe.DataContext is string)
  {
    itemList.ItemsSource = new List<string>() { (string)fe.DataContext };
  }
 
  if (fe.DataContext is IEnumerable<object>)
  {
    itemList.ItemsSource = (fe.DataContext as IEnumerable<object>).Cast<string>();
  }
}

Let me know if you find this code useful, or you apply it in your application.

You can download the full sourcecode here: MarkerClustering.zip

Regards, Colin E.

Suppressing Zoom and Scroll interactions in the Windows Phone 7 WebBrowser Control

November 17th, 2011

This blog post describes a simple helper class that can be used to supress scrolling and pinch zoom of the Windows Phone 7 WebBrowser control.

Developers of Windows Phone 7 application have the full power of IE9 at their disposal in the shape of the WebBrowser control. This control allows you to render both local and remote HTML and JavaScript content within your Silverlight applications. You will find this control used to great effect in RSS readers and Twitter applications such as Rowi, where websites are viewed from within the application rather than by launching the full-blown IE. The WebBrowser control is also used as the host / container for HTML5 based applications, such as Property Finder, the PhoneGap-based application I developed recently.

One problem them people frequently hit against with the WebBrowser control is managing the manipulation events. For example, if you place a WebBrowser control within a Pivot control, how can you pass the horizontal swipe gesture form the WebBrowser to the Pivot?

With HTML5 applications you have a certain amount of control over the browser’s pan and zoom behaviour via the viewport metadata HTML element:

<meta name="viewport" content="user-scalable=no" />

Adding the above meta-tag to a HTML page will prohibit the user from zooming via the pinch gesture. However, it does this in a rather clumsy way – the user can still pinch the page, but when they release, it snaps back to its original scale.

Whilst this might be OK in some contexts, for a HTML5 application where intention is to replicate the feel of a native application as closely as possible, this is just not good enough. Another little browser quirk that has a similar effect is the scroll behaviour. Even if a page fits entirely within the area occupied by the WebBrowser control, they can still scroll the content. Again, it simply snaps back to its original location.

The Solution

I initially thought that there would be no way for me to control the behaviour of the WebBrowser control, it is after-all a very thin .NET wrapper around a native control. However, I stumbled across some StackOveflow answers from quetzalcoatl who had done some digging around in the .NET wrapper and had identified an interesting control called the PanZoomContainer.

If you inspect the visual tree of the WebBrowser control you will find that it is assembled as follows:

\-WebBrowser
  \-Border
    \-Border
      \-PanZoomContainer
        \-Grid
          \-Border (*)
            \-ContentPresenter
              \-TileHost

(visual tree dump generated via the oh-so-useful Linq to VisualTree utility!)

The visual tree is quite simple, composed of a few grids and borders. The significant parts are the TileHost, which is the native IE9 component, and the PanZoomContainer. The TileHost does not handle the mouse manipulation events, these are instead handled by the PanZoomContainer, where they are then translated into gestures (i.e. pinch-zoom) with the result fed back to the TileHost.

What this means is that we can intercept the manipulation events as they bubble up to the PanZoomContainer, cancelling them before they are turned into gestures.

The utility class which I have written handles the events on the Border indicated above. When events are received various conditions are checked to identify pan or scroll interactions, with the events being cancelled accordingly.

The complete class is given below:

using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using LinqToVisualTree;
using Microsoft.Phone.Controls;
 
/// <summary>
/// Suppresses pinch zoom and optionally scrolling of the WebBrowser control
/// </summary>
public class WebBrowserHelper
{
  private WebBrowser _browser;
 
  /// <summary>
  /// Gets or sets whether to suppress the scrolling of
  /// the WebBrowser control;
  /// </summary>
  public bool ScrollDisabled { get; set; }
 
  public WebBrowserHelper(WebBrowser browser)
  {
    _browser = browser;
    browser.Loaded += new RoutedEventHandler(browser_Loaded);
  }
 
  private void browser_Loaded(object sender, RoutedEventArgs e)
  {
    var border = _browser.Descendants<Border>().Last() as Border;
 
    border.ManipulationDelta += Border_ManipulationDelta;
    border.ManipulationCompleted += Border_ManipulationCompleted;
  }
 
  private void Border_ManipulationCompleted(object sender,
                                            ManipulationCompletedEventArgs e)
  {
    // suppress zoom
    if (e.FinalVelocities.ExpansionVelocity.X != 0.0 ||
        e.FinalVelocities.ExpansionVelocity.Y != 0.0)
      e.Handled = true;
  }
 
  private void Border_ManipulationDelta(object sender,
                                        ManipulationDeltaEventArgs e)
  {
    // suppress zoom
    if (e.DeltaManipulation.Scale.X != 0.0 ||
        e.DeltaManipulation.Scale.Y != 0.0)
      e.Handled = true;
 
    // optionally suppress scrolling
    if (ScrollDisabled)
    {
      if (e.DeltaManipulation.Translation.X != 0.0 ||
        e.DeltaManipulation.Translation.Y != 0.0)
        e.Handled = true;
    }
  }
}

Within Property Finder, my HTML5 application, as the user navigates from one page to the next, the JavaScript code notifies the Silverlight container whether the current page should have scrolling disabled, setting the ScrollDisabled property of the above helper class accordingly.

I hope other people find this simple utility class useful. To use it, just cut and paste the code given above. Note, it uses Linq to VisualTree to navigate the WebBrowser visual tree, so you will need to go and grab that also.

Regards, Colin E.

The Untimely Demise of the Plugin (and how LOB developments will suffer)

November 14th, 2011

It has been another interesting week for HTML5 and front-end technologies. We have seen Adobe abandon work on mobile versions of the Flash plugin, news of Silverlight 5 being the last version of the Microsoft plugin and more recently an announcement that Adobe will no longer develop Flex, its enterprise / line-of-business framework for Flash. This blog post looks at how HTML5 seems to be winning the war on plugins …


Image used under creative commons licence from bugmonkey

Earlier this year I collaborated with my colleagues, Graham Odds, a Flex guru, and Chris Price, a HTML5 guru, to write a White Paper comparing Flex, Silverlight and HTML5, which described how front-end technology choice should be based primarily on application complexity, with the two plugin technologies, Flex and Silverlight, being more suited to development at the more complex end of the spectrum.

Despite myself and Graham being strong supporters for Silverlight and Flex respectively, we all agreed that in the future, as HTML5 adoption increased, the tooling would evolve, making HTML5 a good solution for enterprise and Line of Business (LOB) development. However, we all agreed at the time of writing, that the superior development experience offered by Flex and Silverlight made them better choices for complex applications.

Just six months on, my opinion has not changed.

However, it looks like HTML5 is winning the battle for front-end technology domination sooner than I would have expected, and for reasons of politics rather than merit.

Silverlight first appeared four years ago, and has been struggling to gain the almost universal adoption of its direct rival, Flash. The cracks started to show in November last year when Bob Muglia blurted out that “when it comes to Silverlight … our strategy has shifted”. The cracks widened when the Windows 8 blog announced that the new Metro style IE would provide a “plugin-free browsing experience”. The latest rumour is that Silverlight 5 will be the final version of the plugin, which, if true, would be a great shame. The technology is mature and adoption is now greater than 75%, this is the point where Silverlight really should be taking off in a big way.

Flash and Flex have been struggling to make an impact in the smartphone and tablet market, in part due to a highly publicized open letter by Steve Jobs titled “My Thoughts on Flash”, which was an all-out attack on the plugin technology. Despite the damage done by this open letter, Flash was released for Android, and with the Android market share sky-rocketing, the obstacles that Apple put in the way seemed a little less important. Despite this, Adobe announced last week that they would cease working on the mobile Flash plugin. Whilst this does not mean that Adobe has dropped Flash altogether, the news is not so good for Flex, the enterprise library built on top of Flash. Previously there had been a similar announcement to Bob Muglia’s from the Flex team “Recognizing the role of Flex has changed”, however, this weekend Adobe announced that Flex development was being handed over to the open source community, “In the long-term, we believe HTML5 will be the best technology for enterprise application development.”

I do not object to HTML5 itself, and have had a lot of fun recently creating the first HTML5 based Windows Phone 7 application. However, I feel that both Microsoft and Adobe are being a bit hasty in sacrificing their own plugin technologies in favour of HTML5. Both are heavily investing in HTML5 tooling, Adobe Edge is proving to be a great tool for creating HTML5 timeline animations, whilst Microsoft has embraced HTML5 as part of Windows 8. However, there is one great big elephant in the room …

HTML5 is not ready for enterprise development!

I have written about the problems with HTML5 and JavaScript in earlier blog posts:

If you have tried to create large complex applications with JavaScript and a browser-based user-interface, you will know what I am talking about! Interestingly, I recently read an article about node.js, which is proving to be a popular server-side technology, the author noted:

“Node.js is a server-side JavaScript execution engine. The actual language isn’t important – certainly if I had a time machine and Tony Stark’s cash I’d go back and change it so that it wasn’t based on JavaScript.” – Matthew Baxter-Reynolds

It appears to me that Microsoft and Adobe are concentrating too much on the smartphone and tablet market. I agree that for many consumer facing sites HTML5 is a good technology choice, especially when you consider that this technology works well on most tablets and smartphones. It is also more readily able to adapt to the device it is being used on, for example, Android phones have a nice feature where they re-flow HTML documents to better fit the screen. However, for more complex enterprise applications where tablets and smartphones are not the target platform, Flex and Silverlight are still a much better option.

It is true that smartphones and tablets are becoming much more commonplace, you see no end of people playing with iPads, Androids and the occasional Windows Phone 7 on the train. But you can bet that every single one of those people when they get to work will put their smartphone in their pocket, their tablet in their bag and will sit down to a standard desktop computer, monitor, mouse and keyboard. The more I think about it, the Windows 8 split-personality makes sense.

Unfortunately it looks like Adobe and Microsoft have lost sight of the enterprise and of the desktop user. If I were to embark on a new line-of-business project for a company intranet the technology choice would be very difficult right now. I would like to choose Silverlight or Flex, but would either be a wise technology choice if you know that the current version might be the last?

Regards, Colin E.