Colin Eberhardt's Technology Adventures

An Introduction to Semantic Zoom in Windows 8 Metro

November 10th, 2011

With the new Metro UI, Windows 8 has firmly embraced the tablet form-factor, with the interface tailored for touch and multi-touch interactions. Many of the Metro concepts familiar to Windows Phone 7 (WP7) developers are also present in Windows 8. Watching the vieos from the //build/ conference you can see how the team responsible for Win8 Metro have really thought about the tablet experience, with new concepts such as cross-slide and transitions being added to the familiar XAML environment.

One of the most intersting new concepts introduced at build was ‘semantic zoom’, where a pinch gesture, which is more usually assocaited with optical zoom, is used to switch between different view of the same data. Because a pinch is conceptually a zoom operation, this is most typically used for switching from a more detailed view to a summary, or top-level view. Interestingly, with WP7 we have already seen the semantic zoom concept (minus the pinch gesture). The jump list control provides a mechanism for jumping from one location within a list to another, via jump-buttons. This solves the problem of navigating long lists using swipe gestures. However, semantic zoom does not have to be restricted to jump-list style scenarios, for more ideas see the Microsoft Guidelines for Semantic Zoom.

I thought that a fun way to familiarise myself with semantic zoom would be to use it to create a WP7 style jump list experience in Win8 Metro, you can see a (very poorly filmed) video of my creation below …

The JumpViewer control provides semantic zoom functionality. This control allows you to specify two views, which would most typically be as follows:

  • ContentView - the detail view.
  • JumpView - the top-level, or category view.

However, you can provide any two views of your data, as long as there is some meaningful (or semantic!) relationship between the two.
Both views must implement IJumpViewerInfo (As an aside, WinRT makes much better use of interfaces than the .NET XAML UIs), which has methods that allows the JumpViewer to instruct a view to make a certain item visible. This allows the two views to colaborate in order to allow navigation. The GridView control, which is part of the WinRT framework, implements this interface, so is a suitable control to use for both views.

The ContentView

So let’s create a content view that renders a jump-list style control. You specify each of the view as follows:

<JumpViewer x:Name="jumpViewer">
  <JumpViewer.JumpView>
    <GridView>
      ...
    </GridView>
  </JumpViewer.JumpView>
  <JumpViewer.ContentView>
    <GridView>
       ...
    </GridView>
  <JumpViewer.ContentView>
</JumpViewer>

The JumpViewer requires that you supply a CollectionViewSource to the JumpView in order to render the top-level view. Also, I have found that whereas the ContentView inherits the JumpViewer DataContext, making databinding possible, the JumpView does not. This results in a rather clumsy syntax for supplying data to this control:

var cvs = new CollectionViewSource();
cvs.Source = source;
jumpViewer.DataContext = cvs;
(jumpViewer.JumpView as ListViewBase).ItemsSource = cvs.View.CollectionGroups;

It would be possible to wrap up the above code in an attached behaviour, to allow binding without code-behind, but it is not really worth the effort. I am sure this will be tidied up in future versions!

The ContentView is a GridView which by default scrolls its content horizontally. For my jump-list I wanted to scroll vertically, this can be achieved by setting the following attached properties:



The template for each item, and the panel that the GridView uses to host the items it generates are specified as follows:

<GridView.ItemTemplate>
    <!-- the template for each data item -->
    <DataTemplate>
        <StackPanel Orientation="Horizontal" Width="500">
            <TextBlock Text="{Binding Path=Surname}" FontSize="35"/>
            <TextBlock Text=", " FontSize="35"/>
            <TextBlock Text="{Binding Path=Forename}" FontSize="35"/>
        </StackPanel>
    </DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
    <!-- the top-level container -->
    <ItemsPanelTemplate>
        <StackPanel Orientation="Vertical" />
    </ItemsPanelTemplate>
</GridView.ItemsPanel>

The GridView has built-in support for grouping. In order to use this, the data supplied to the ItemsSource must be a collection of IGroupInfo instances. The WinRT framework does not supply a default implementation of this interface, however, it is very easy to implement by extending a list to add a Key property:

public class GroupInfoList : List<object>, IGroupInfo
{
  public GroupInfoList(object key, IEnumerable<object> values)
  {
    Key = key;
    AddRange(values);
  }
 
  public object Key { get; set; }
 
  public new IEnumerator<object> GetEnumerator()
  {
    return (System.Collections.Generic.IEnumerator<object>)base.GetEnumerator();
  }
}

This example renders Person instances (which have Surname and Forename properties). We can group them using a Linq query, to create a sorted list of IGroupInfo instances as follows:

var source = people.GroupBy(person => person.Surname.Substring(0, 1),
                               (key, items) => new GroupInfoList(key, items))
                   .OrderBy(group => group.Key);

When supplied with grouped data, you can configure how the GridView renders it via the GroupStyle property:

<GridView.GroupStyle>
    <GroupStyle>
        <GroupStyle.HeaderTemplate>
            <!-- the header for each grup -->
            <DataTemplate>
                <Button Content="{Binding Name}"
                        Width="80" Height="80"
                        Foreground="Green" BorderBrush="Green"
                        FontSize="40"
                        VerticalContentAlignment="Bottom"
                        Click="Button_Click"/>
            </DataTemplate>
        </GroupStyle.HeaderTemplate>
        <GroupStyle.ContainerStyle>
            <!-- the 'body' of each group -->
            <Style TargetType="GroupItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="GroupItem">
                            <StackPanel Orientation="Vertical">
                                <!-- renders the header -->
                                <ContentPresenter Content="{TemplateBinding Content}" />
                                <!-- renders the items within this group -->
                                <ItemsControl x:Name="ItemsControl2" ItemsSource="{Binding GroupItems}" />
                            </StackPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </GroupStyle.ContainerStyle>
        <GroupStyle.Panel>
            <!-- the panel used to render the items within the group -->
            <ItemsPanelTemplate>
                <VariableSizedWrapGrid Orientation="Horizontal"   MaximumRowsOrColumns="1"/>
            </ItemsPanelTemplate>
        </GroupStyle.Panel>
    </GroupStyle>
</GridView.GroupStyle>

This rather complex block of XAML is composed of three parts, the best way to understand how they relate to the rendered output is via a picture:

The JumpView

The jump-view is again a GridView, however, this view is much simpler because it does not render a grouped view. The ItemsSource for the GridView is set to the CollectionViewSource.View.CollectionGroups property. At runtime this property is populated with a collection of DependencyObjects which have a Name property which exposes the Key of each group.

The XAML for the JumpView is given below:

<GridView Foreground="White"
            HorizontalAlignment="Center" VerticalAlignment="Center">
    <GridView.ItemContainerTransitions>
        <!-- disable the default entrance transitions -->
        <TransitionCollection/>
    </GridView.ItemContainerTransitions>
    <GridView.ItemTemplate>
        <!-- the template for each 'category' button -->
        <DataTemplate>
            <TextBlock  Text="{Binding Name}"
                        FontFamily="Segoe UI Light"
                        FontSize="55"
                        Foreground="White" />
        </DataTemplate>
    </GridView.ItemTemplate>
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapGrid ItemWidth="120" ItemHeight="120"
                        Orientation="Horizontal"
                        MaximumRowsOrColumns="4" VerticalChildrenAlignment="Center" />
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="Margin" Value="4" />
            <Setter Property="Padding" Value="10" />
            <Setter Property="Background" Value="Green" />
            <Setter Property="HorizontalContentAlignment" Value="Center" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
        </Style>
    </GridView.ItemContainerStyle>
</GridView>

The JumpViewer handles the pinch zoom, hiding the ContentView and showing the JumpView, which looks as follows:

When you click on one of the items displayed in the above view, the JumpViewer detects the item that has been clicked, it then shows the ContentView and instructs it to scroll to show the first item within the clicked group. This works regardless of the layout you define for the ContentView, i.e. it will scroll vertically or horizontally depending on the layout.

The WP7 jump-list switches to the jump-view when the user clicks a category heading. The final step in emulating this control is to handle the click on the category heading and programmatically switch views as follows:

private void Button_Click(object sender, RoutedEventArgs e)
{
  jumpViewer.IsContentViewActive = false;
}

In summary, the JumpViewer is a powerful and flexible control which makes it easy to provide semantic-zoom within Metro applications.

You can download the full sourcecode: SemanticZoomExample.zip

Regards, Colin E.

WinRT Transitions – Creating Fast and Fluid Metro UIs

October 10th, 2011

This blog post looks at the new concept of ‘transitions’ that WinRT, within Windows 8, introduces. This concept makes it very easy for you to create a fluid and interactive UI without going anywhere near storyboards!

I have to admit it, I am a big fan of the Metro Design Language; the clean chrome-free graphics, combined with typography inspired by signage and designed for maximum legibility, has given Windows Phone 7 an instantly recognisable style. However, whilst Silvelight applications developed for Windows Phone 7 look like the native metro phone apps, they do not move like them. Motion is an important part of metro, with one of the tenants being that it is “Alive In Motion”. This lead me to publish a multi-part blog series, called Metro In Motion, which provided implementations of the various native animations and transitions (1, 2, 3, 4, 5, 6 & 7).

Windows 8 again is built using the Metro Design Language, and again the tools make it easy for you to create an application which has the Metro look. However, where Windows 8 differs from Windows Phone 7 is that developer APIs make it very easy for you to create applications that move like a Metro application. WinRT introduces a new XAML concept, transitions, which is the focus of this blog post.

I was inspired to explore transitions after watching John Papa’s excellent //build/ session “Stand out with styling and animation in your XAML app”, I would thoroughly encourage you to watch it!

I have a (very shaky) video of the demo code included with this blog post below:

With WinRT UIElement, the base class for any element that is added to the visual tree (UI), has a Transitions property. You can use this to specify a collection of transitions for an individual element:

<Rectangle>
    <Rectangle.Transitions>
        <TransitionCollection>
            <RepositionThemeTransition/>
            <EntranceThemeTransition/>
        </TransitionCollection>
    </Rectangle.Transitions>
</Rectangle>

Each transitions is an animation that the WinRT framework plays in response to certain ‘events’. We’ll take a look at the various transitions in turn…

EntranceThemeTransition

When elements are added to the UI they appear instantly (as you might expect), however, with the EntranceThemeTransition elements gracefully fade and slide into location. This animation will fire whenever your element is added to the UI, this could be as a result of:

  • The application first loading, any element with this transitions specified will animate when the application starts.
  • The element being added programatically or as a result of binding (e.g. within a bound list)
  • The element being added as a child of another element

Basically, you can rely on this animation being fired whene your element is first visible to the user.

ChildrenTransitions

If you add multiple elements to the UI at the same time, or a UserControl is loaded that contains a number of animated elements, their EntranceThemeTransition animations will fire at the same time. However, if you have a panel that contains a number of elements, you can make them appear in sequence by adding the EntranceThemeTransition to the ChildrenTransitions property of the panel. For example, any element added to the WrapGrid defined below, will animate as it appears:

<WrapGrid>
    <WrapGrid.ChildrenTransitions>
        <TransitionCollection>
            <EntranceThemeTransition/>
        </TransitionCollection>
    </WrapGrid.ChildrenTransitions>
</WrapGrid>

Here we can see nine rectangles being animated as they slide into location:

The animations are fired in sequence, no matter how the elements are added to the panel. For example, if you add a couple of elements in code-behind, they will appear in sequence.

(As an aside, this is really quite cool, I can imagine how I might implement the animations on individual elements in WPF / Silverlight, however, the child-transitions concept would be vary hard to implement!)

One thing to note is that the framework fires the animations in the order that elements appear in the Panel.Children collection, this is not necissarily the order they appear in the UI. For example, the following example shows elements added in a Grid, where the elements are defined in a random order within XAML:

This doesn’t look quite so good!

RepositionThemeTransition

When you change the positions of an element, either directly or as a result of its parent layout changing, it will move into its new location instantly. By adding a RepositionsThemeTransition, the WinRT framework will animate the element as its moves location.

A few things I have observed:

  • The RepositionThemeTransition is not fired when you apply a RenderTransform to an element, possibly because transitions are applied via render transforms
  • The RepositionThemeTransition is not fired when an elements position within a Canvas changes. This is probably because Canvas.Left and Canvas.Top do not have any impact on layout.
  • This transition, and none of the others discussed in this blog post, work when the elements are hosted within a FlipView (not sure why, this feels like a bug)

Again, if you add a RepositionThemeTransition to the children of a panel via ChildrenTransitions, the animations are fired in sequence. In the below image an element has been removed from the top of a WrapGrid:

(Trust me – this looks much better in action! try running the sample code with this blog-post)

One point of interest, you can simplify the XAML for adding ChildrenTransitions when using an ItemsControl (Or ListBox, GridView or other subclasses) from the following:

<ItemsControl Grid.Row="1"
                x:Name="itemsTwo">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapGrid>
                <WrapGrid.ChildrenTransitions>
                    <TransitionCollection>
                        <RepositionThemeTransition/>
                    </TransitionCollection>
                </WrapGrid.ChildrenTransitions>
            </WrapGrid>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

By using the ItemContainerTransitions property as follows:

<ItemsControl Grid.Row="1"
                x:Name="itemsTwo">
    <ItemsControl.ItemContainerTransitions>
        <TransitionCollection>
            <RepositionThemeTransition/>
        </TransitionCollection>
    </ItemsControl.ItemContainerTransitions>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapGrid/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

This property specifies a collection of transitions that will be set on the item-container.

AddDeleteThemeTransition

This transitions is intended for use with panels, it animates the process of adding / removing elements. This transitions is added to the ChildrenTransitions (or ItemContainerTransitions) just like the others. As items are added, they fade into place, and as they are removed they slide out and the other elements reposition themselves. One thing that is really cool about this transitions is that it ‘understand’ the layout of the panel.

Here you can see elements being repositioned within a WrapGrid when the first element is being removed. You can see that the elements are moved in batches based on their row / column:

Very cool!

ContentThemeTransition

The final transition I want to look at is the ContentThemeTransition. For those of you who have worked with Silverlight or WP7, this provides the same functionality as the popular TransitioningContentControl from the Slverlight toolkit – now this feature is delivered by the framework itself (at last!)

To enable this, add a ContentThemeTransition to the ContentTransitions property (note, not, the Transitions property!).

<ContentControl x:Name="contentCtrl>
    <ContentControl.ContentTransitions>
        <TransitionCollection>
            <ContentThemeTransition HorizontalOffset="200"/>
        </TransitionCollection>
    </ContentControl.ContentTransitions>
</ContentControl>

Whenever the content of this control is changed, either by setting the Content property directly, or as a result on the databound content changing, the new content gracefully slides into place.

Conclusions

Whilst WinRT borrows much from Silverlight and WPF, it brings some great new controls and features and transitions are probably one of my favourite new features. They really help you create fluid applications with minimum effort.

I have covered all the theme-transitions except one, ReorderThemeTransition, I have not yet found out how to use this transition, I think this might be because of a lack of sorting / filtering of collections in WinRT. This is probably functionality that will be added at a later date.

You can download the full sourcecode for this blog post: WinRTTransitions.zip

Regards, Colin E

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.

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.