Colin Eberhardt's Adventures in .NET

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.

A Windows Phone 7.1 (Mango) MVVM Tombstoning Example

October 3rd, 2011

In this blog post I look at the new application lifecycle model that Windows Phone 7.1 (Mango) introduces, and show how to handle the various lifecycle events in a simple MVVM application.

In a previous blog post I described the development of a simple Windows Phone 7 application using the Model-View-ViewModel (MVVM) pattern and how to handle application lifecycle events, specifically tombstoning. In this blog post I will look at updating this sample for Windows Phone 7.1 (Mango) to handle fast-app switching and the new Dormant state.

As an aside, I have bumped into a number of developers who were under the impression that Mango removes the need to tombstone. I’m afraid that this is not the case!

Introduction and a Recap

The WP7 application lifecycle model is designed to allow the user to move fluidly between applications whilst minimising the memory that these applications consume. Neither WP7 or WP7.1 (Mango) allow true multi-tasking, where more than one application is running at the same time. Instead, they employ a number of techniques to give the illusion of multi-tasking. As a developer you are exposed to this via the application lifecycle.

The WP7 (pre-Mango) application lifecycle, with the events that are fired for each transition, is illustrated below:

You can see the tasks that you must perform under each state transition.

When the user pressed the Start button, your application stops running and becomes tombstoned. It is your responsibility to save your application state such that when the user pressed the back-button (navigating the back-stack) to return to your application, it is in the same state as when they left it. This involves saving both the state of your application logic, within the application PhoneApplicationService.State dictionary and the UI state (e.g list scroll position) within the PhoneApplicationPage.State dictionary for each page. I described how you can achieve this in my previous blog post. I also want to point out that the rather tricky task of saving UI state can be made much simpler by using things like Matt Lacey’s popular Tombstoning Helper utilities.

One other thing that the Execution Model Overview on MSDN does not make terribly clear is that the back-stack has a limited size. What this means is that your tombstoned application state will not necessarily be re-activated. You can test this by running your application with the debugger attached, if you create a long enough back-stack (by repeatedly hitting the Start button and starting a new application), your debugging session will eventually terminate. This occurs when your application state has been pushed off the back-stack.

The upshot of this is that when your application is tombstoned you must save your state to isolated storage as well as the State dictionary (because your application might never get closed). Although, as it is recommended that your application tombstones swiftly, you might want to store application state to isolated storage at more regular intervals.

Windows Phone 7.1 Lifecycle

Windows Phone 7.1 (Mango) introduces a much more convincing illusion of multi-tasking, where the user can press and hold the back-button to rapidly switch between a small number of apparently concurrently running applications. However, again this is not true multitasking.

To achieve this, WP7.1 introduces a new state in the application lifecycle, the Dormant state:

When you application is in a Dormant state it is still in memory, however it is not executing. This allows the phone to re-start your application much more rapidly than when it is in a tombstoned state.

The phone can only store a limited number of dormant applications, as a result it might choose to move your application from a dormant to a tombstoned state. You do not receive any notification that this has occurred. However, the tombstoning and re-activiation process is the same as that for WP7.0, where you re-activate your application from the data saved in the State dictionary.

Again, there is always the possibility that your tombstoned data will be pushed out of the back-stack and lost.

Notice that there have not been any new events added to indicate that your application has returned from a Dormant state, your application will always receive Activated event when it is restarted regardless of whether it was dormant or tombstoned. This is good news, because it is backwards compatible with WP7.0.

Handling the Dormant State

So how do you handle this new state in your application? In order to maintain backwards compatibility you don’t strictly have to, however it is actually very simple to do.

The event arguments passed to the Activated event now have a new IsApplicationInstancePreserved argument which is true if your application has been re-started from a dormant state and false if it has been restarted from a tombstoned state. If this argument returns true, you simply do nothing! This ensures that you do not perform any redundant actions and helps your application restart faster.

The code below is all of the lifecycle code (apart from per-page UI state) that my MVVM example application contains. You can see that the code for a re-activated dormant state does nothing.

// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
  Debug.WriteLine("Launching");
 
  LoadViewModelFromIsolatedStorage();
 
  // if the view model is not loaded, create a new one
  if (ViewModel == null)
  {
    ViewModel = new FeedViewModel();
    ViewModel.Update();
  }
 
  // set the frame DataContext
  RootFrame.DataContext = ViewModel;
}
 
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
  if (e.IsApplicationInstancePreserved)
  {
    Debug.WriteLine("Activated From Dormant State");
  }
  else
  {
    Debug.WriteLine("Activated From Tombstoned State");
 
    LoadViewModelFromAppState();
 
    RootFrame.DataContext = ViewModel;
  }      
}
 
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
  Debug.WriteLine("Deactivated");
  SaveViewModelToAppState();
  SaveViewModelToIsolatedStorage();
}
 
// Code to execute when the application is closing (eg, user hit Back)
// This code will not execute when the application is deactivated
private void Application_Closing(object sender, ClosingEventArgs e)
{
  Debug.WriteLine("Closing");
  SaveViewModelToIsolatedStorage();
}
 
private void SaveViewModelToAppState()
{
  PhoneApplicationService.Current.State[ModelKey] = ViewModel;
}
 
private void LoadViewModelFromAppState()
{
  if (PhoneApplicationService.Current.State.ContainsKey(ModelKey))
  {
    ViewModel = PhoneApplicationService.Current.State[ModelKey] as FeedViewModel;
  }
}
 
private void LoadViewModelFromIsolatedStorage()
{
  // load the view model from isolated storage
  using (var store = IsolatedStorageFile.GetUserStoreForApplication())
  using (var stream = new IsolatedStorageFileStream("data.txt", FileMode.OpenOrCreate, FileAccess.Read, store))
  using (var reader = new StreamReader(stream))
  {
    if (!reader.EndOfStream)
    {
      var serializer = new XmlSerializer(typeof(FeedViewModel));
      ViewModel = (FeedViewModel)serializer.Deserialize(reader);
    }
  }
}
 
private void SaveViewModelToIsolatedStorage()
{
  // persist the data using isolated storage
  using (var store = IsolatedStorageFile.GetUserStoreForApplication())
  using (var stream = new IsolatedStorageFileStream("data.txt",
                                                    FileMode.Create,
                                                    FileAccess.Write,
                                                    store))
  {
    var serializer = new XmlSerializer(typeof(FeedViewModel));
    serializer.Serialize(stream, ViewModel);
  }
}

Debugging Dormant and Tombstoned state

Because the application now has a couple of states that it can be in when it is not running, you need to employ a few tricks to be able to force the application into these states while developing. Typically the emulator will always place your application into a dormant state when you hit the Start button.
The fast-app switch UI is not enabled on the emulator by default, however you can enable it with a bit of hackery as described on this forum thread.

So, if your application always moves into a dormant state, how do you test your tombstoning code? Fortunately there is a build setting which you can enable which will ensure that your application is always tombstoned rather than placed in a dormant state:

You should test your application with this setting enabled and disabled to ensure that it accommodates tombstoning and dormant state.

I hope this blog post has helps understand the slightly complex application lifecycle that Mango introduces. It is clear that this delivers the best performance to the user, however it is a little complex for the developer!

You can download my updated MVVM example here: WindowsPhoneMVVMExample.zip

Regards, Colin E.