10 August 2012

A WinRT behavior to turn a FlipView into a kind of Windows 8 Panorama

IC425813(UPDATED for RTM August 18 2012)
One of the most beautiful controls of Windows Phone is the Panorama. It’s ideal for showing a lot related content on a small screen and enable the user to easily pan trough it. A visual cue for ‘there is more’ is provided by showing a little part of the next panel to the very right of the current data. A typical example is showed right.

It’s also one of the most abused controls (guilty as charged Your Honor), but still I wanted to port Catch’em Birds to Windows 8 – and I found out there was no ready-to-use control. After fighting with ScrollViewers and GridViewers and whatnot I came to this very simple behavior, which basically takes a FlipView and hammers it into a kind of Panorama.

Now the FlipView is designed to be a full-screen control so the behavior basically walks past all the items in the FlipView, shrinks them horizontally by a configurable percentage of the screen, and displaces the ‘next’ panel a little to the left (making it appear at the right side of the screen on the current panel). To make this look a little bit more fast and fluid, I have made the displacement itself animated, so that the ‘next’ screen not so much snaps as glides into view. The overall effect looks pretty nice to me. I hope Microsoft will think so as well, as my app is up for an App Excellence Lab soon ;-)
In my app it looks like this. I still lack a decent screen recorder for Windows 8, so I took out the video camera

So this behavior, most originally called “FlipViewPanoramaBehavior” is of course based upon my earlier WinRtBehaviors CodePlex project. It starts out like this, with the following dependency properties:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Win8nl.External;
using Win8nl.Utilities;
using WinRtBehaviors;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;

namespace Win8nl.Behaviors
{
  /// <summary>
  /// A behavior to turn a FlipView into a kind of panorama
  /// </summary>
  public class FlipViewPanoramaBehavior : Behavior<FlipView>
  {
    #region AnimationTime

    /// <summary>
    /// AnimationTime Property name
    /// </summary>
    public const string AnimationTimePropertyName = "AnimationTime";

    public int AnimationTime
    {
      get { return (int)GetValue(AnimationTimeProperty); }
      set { SetValue(AnimationTimeProperty, value); }
    }

    /// <summary>
    /// AnimationTime Property definition
    /// </summary>
    public static readonly DependencyProperty AnimationTimeProperty = 
      DependencyProperty.Register(
        AnimationTimePropertyName,
        typeof(int),
        typeof(FlipViewPanoramaBehavior),
        new PropertyMetadata(250));

    #endregion

    #region NextPanelScreenPercentage

    /// <summary>
    /// NextPanelScreenPercentage Property name
    /// </summary>
    public const string NextPanelScreenPercentagePropertyName = 
      "NextPanelScreenPercentage";

    public double NextPanelScreenPercentage
    {
      get { return (double)GetValue(NextPanelScreenPercentageProperty); }
      set { SetValue(NextPanelScreenPercentageProperty, value); }
    }

    /// <summary>
    /// NextPanelScreenPercentage Property definition
    /// </summary>
    public static readonly DependencyProperty NextPanelScreenPercentageProperty = 
      DependencyProperty.Register(
        NextPanelScreenPercentagePropertyName,
        typeof(double),
        typeof(FlipViewPanoramaBehavior),
        new PropertyMetadata(10.0));
    #endregion
  }
}
So “AnimationTime” is the number of milliseconds the behavior takes to glide the next panel into view, and NextPanelScreenPercentage is an indication of how much screen real estate the next panel will take. Nothing special here yet.

If I want to muck around with a FlipView contents, I first have to find these contents. With some breakpoints and watches I found out I could use the following code to find the FlipViewItems:
/// <summary>
/// Find all Flip view items
/// </summary>
/// <returns></returns>
private List<FlipViewItem> GetFlipViewItems()
{
  var grid = AssociatedObject.GetVisualChildren().FirstOrDefault();
  if (grid != null)
  {
    return grid.GetVisualDescendents().OfType<FlipViewItem>().ToList();
  }
  return null;
}
Attentive readers might observe that neither GetVisualChildren nor GetVisualDescendents are part of the WinRT api, which is perfectly correct – they come from the VisualTreeHelperExtensions I ported from Windows Phone some time ago. Don’t start to download this stuff and build it together yourself – wait till the end and I will show the lazy way to do this.

Anyway – I wanted to move the FlipView’s contents fluently. That means I will use some Storyboards to work on Translations. So we identify the contents of each FlipViewItem and set its fist visual child’s Rendertransform to CompositeTransform, if that’s not already present:
/// <summary>
/// At compositions transforms to every item within every flip view item
/// </summary>
private void AddTranslates()
{
  var items = GetFlipViewItems();
  if (items != null && items.Count > 1)
  {
    foreach (var item in items)
    {
      var firstChild = item.GetVisualChild(0);
      if (!(firstChild.RenderTransform is CompositeTransform))
      {
        firstChild.RenderTransform = new CompositeTransform();
        firstChild.RenderTransformOrigin = new Point(0.5, 0.5);
      }
    }
  }
}
This assumes every FlipViewItem contains just one child. You better make sure it does for this to work, so put a Grid around it if you need more than one thing to sit in there.

Now the core of the whole behavior is this one piece of code:
/// <summary>
/// Does the actual repositioning and sizing of the items displayed in the Flipview
/// </summary>
private void SizePosFlipViewItems()
{
  AddTranslates(); // <-- moved from AssociatedObjectLoaded for RTM
  var size = AssociatedObject.ActualWidth*(NextPanelScreenPercentage/100);
  var shift = size - 15;

  var items = GetFlipViewItems();
  if (items != null && items.Count > 1)
  {
    // Make all items a bit smaller and make sure they are aligned left
    foreach (var item in items)
    {
      item.GetVisualChild(0).HorizontalAlignment = HorizontalAlignment.Left;
      item.GetVisualChild(0).Width = items[0].ActualWidth - size;
    }

    var selectedIndex = AssociatedObject.SelectedIndex;

    if (selectedIndex > 0)
    {
      StartTranslateStoryBoard(0, 0, 
                               items[selectedIndex - 1].GetVisualChild(0), 0);
    }

    StartTranslateStoryBoard(0, 0, items[selectedIndex].GetVisualChild(0), 
                             AnimationTime);

    if (selectedIndex + 1 < items.Count)
    {
      StartTranslateStoryBoard(-shift, 0,
                                items[selectedIndex + 1].GetVisualChild(0), 
                                AnimationTime);
    }
  }
}
First it calculates the new size of the FlipViewItems, and then it calculates how much it can shift the ‘next panel’ – basically, how much room is there between this panel and the next. This is currently a hard coded number, but feel free to make that a property as well ;-).

Then, for every FlipViewItem it makes the first visual child “size” smaller, and makes sure it’s aligned to the left (so space comes free and the right side). Then:
  1. It moves the panel that just disappeared to the left (if any) back  to it’s normal position, in no time (i.e. not animated – it’s invisible to the leftanyway, so why bother).
  2. It moves the current panel to its normal position, but it animates it. This is because if it’s moved in from the left, it moves a bit too far, as you might have noticed in the movie – so it glides back
  3. It moves the next panel (if any) a little bit to the left – animated, so it glides into view on the right hand side of the screen.
Now of course there is the slight matter of the method that make the storyboards to make it happen:
private static void StartTranslateStoryBoard(double desiredX, double desiredY, 
                                             FrameworkElement fe, int time)
{
  var translatePoint = fe.GetTranslatePoint();
  var destinationPoint = new Point(desiredX, desiredY);
  if (destinationPoint.DistanceFrom(translatePoint) > 1)
  {
    var storyboard = new Storyboard { FillBehavior = FillBehavior.HoldEnd };
    storyboard.AddTranslationAnimation(
         fe, translatePoint, destinationPoint,
         new Duration(TimeSpan.FromMilliseconds(time)),
         new CubicEase { EasingMode = EasingMode.EaseOut });
    storyboard.Begin();
  }
}
Once again, I use some extension methods from code ported from Windows Phone in the article I mentioned before, I underlined them to make them distinguishable from the standard API. Basically: this method accepts a FrameworkElement and moves it to a desired position in a desired time, using a storyboard that animates a translation. That is to say, unless it is already in that desired position. I think I will make this into a separate extension method in a utilities library one day but for the moment it’s doing fine.

All that’s left now is some wiring up, I cobbled that all together in one code block:
protected override void OnAttached()
{
  AssociatedObject.Loaded += AssociatedObjectLoaded;
  base.OnAttached();
}
protected override void OnDetaching()
{
  AssociatedObject.Loaded -= AssociatedObjectLoaded;
  AssociatedObject.SelectionChanged -= AssociatedObjectSelectionChanged;
  AssociatedObject.SizeChanged -= AssociatedObjectSizeChanged;
}

private void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
{
  //AddTranslates(); deleted for RTM
  SizePosFlipViewItems();
  AssociatedObject.SelectionChanged += AssociatedObjectSelectionChanged;
  AssociatedObject.SizeChanged += AssociatedObjectSizeChanged;
}

private async void AssociatedObjectSelectionChanged(object sender, 
                                                    SelectionChangedEventArgs e)
{
  await Task.Delay(250); // Updated after bug report from SCPRedMage
  SizePosFlipViewItems();
}

private async void AssociatedObjectSizeChanged(object sender, 
                                               SizeChangedEventArgs e)
{
  await Task.Delay(250);
  SizePosFlipViewItems();
}
OnAttached and OnDetaching do their usual basic wiring and unwiring of events.
When the AssociatedObject (i.e. the FlipView) is first loaded the FlipViewItems’ first child gets their CompositeTransforms, then the initial screen layout is created by calling SizePosFlipViewItems. Then two events are wired up:
  • SelectionChanged
  • SizeChanged
Now the first one is logical – when the user selects the next panel (i.e. he scrolls it in from the left or right) the panels need to be arrange again so that the newly selected panel stays in view (it scrolls too much to the right, remember) and the ‘new’ next panel comes into view at the left hand side of the screen.

The SizeChanged intercept is necessary for when the user rotates his screen or snaps the application. For then the size of the screen changes, and the portion of the screen that the next panel may use is considerably smaller – in pixels. In my app this is taken care of by a Visual State Manager that listens to page events – basically something stolen from the LayoutAwarePage that’s in every template project – but that takes a while. Now I know I am going to be lambasted for this (and I have a pretty good idea by whom), but to solve this the SizeChanged handler waits a bit for actually calling SizePosFlipViewItems. And to prevent UI blocking I interestingly abused Task.Delay for that. It’s crude, but it works. As you may have seen in the movie when I snapped the app.

So there you have it. The code works, you have seen it in action. Its usage is ridicilously simple: make a FlipView, add items, and add this behavior to the FlipView. Done. You can download the the behavior here but you will need quite some base libs to get it working – as it uses a lot of my win8nl library on CodePlex. If you want to go the easy and quick way: just use the Win8nl NuGet package. That will get you the behavior and all the prerequisites, including MVVMLight.

Be aware that win8nl now uses the Reactive extensions. They are included in the NuGet package and they will come with it as a dependency

UPDATE: Please note there is a tiny code change since original publication: due to Microsoft optimizing the FlipView not all elements are initially loaded, so the check if every FlipViewItem child has a CompositeTransform has to performed at every manipulation.

UPDATE 2: There's a tiny update to AssociatedObjectSelectionChanged. And those who want a simple working sample, download the sources from codeplex and fire up FlipViewTest.XAML as the start page.

24 July 2012

A WinRT behavior to mimic EventToCommand

This is deprecated. Please use the official behaviors SDK now. See here for a simple migration guide

Updated September 2 2012 with two sample applications

Whoever used the MVVMLight framework by Laurent Bugnion is familiar with EventToCommand, especially those who used it on Windows Phone before the advent of 7.5 (or SDK version 7.1.1). This utility enables triggers to fire commands on the viewmodel

Unfortunately, in WinRT there is no such thing as a trigger, so this method cannot be used, and MVVMLight for WinRT therefore does not include EventToCommand. This morning I got a DM from AESIR Consultancy asking me if I had a solution for that, because he had some trouble porting a Windows Phone app to Windows 8. I did not have it then, but I had the feeling it could be done quite easily. In the afternoon, in the time between I came home and my wife came home from work I wrote this behavior that basically does the same as EventToCommand.

And indeed, WinRT does not support behaviors either, but that has been solved already. In order for this to work, you will need to download the NuGet WinRtBehaviors package

Anyway – the behavior. You start out with a project with references to WinRtBehaviors and the Reactive extensions – ReSharper 7 EAP found them readily and automatically attached references to my projects which now has the following references to reactive stuff:

  • System.Reactive.Core
  • System.Reactive.Interfaces
  • System.Reactive.Linq
  • System.Reactive.PlatformServices

So we start out with the class definition and some boring Dependency property definitions:

using System;
using System.Reactive.Linq;
using System.Reflection;
using System.Windows.Input;
using WinRtBehaviors;
using Windows.UI.Xaml;

namespace Win8nl.Behaviors
{
  /// <summary>
  /// A behavior to imitate an EventToCommand trigger
  /// </summary>
  public class EventToCommandBehavior : Behavior<FrameworkElement>
  {

    #region Event

    /// <summary>
    /// Event Property name
    /// </summary>
    public const string EventPropertyName = "Event";

    public string Event
    {
      get { return (string)GetValue(EventProperty); }
      set { SetValue(EventProperty, value); }
    }

    /// <summary>
    /// Event Property definition
    /// </summary>
    public static readonly DependencyProperty 
      EventProperty = DependencyProperty.Register(
        EventPropertyName,
        typeof(string),
        typeof(EventToCommandBehavior),
        new PropertyMetadata(default(string)));

    #endregion

    #region Command

    /// <summary>
    /// Command Property name
    /// </summary>
    public const string CommandPropertyName = "Command";

    public string Command
    {
      get { return (string)GetValue(CommandProperty); }
      set { SetValue(CommandProperty, value); }
    }

    /// <summary>
    /// Command Property definition
    /// </summary>
    public static readonly DependencyProperty 
      CommandProperty = DependencyProperty.Register(
        CommandPropertyName,
        typeof(string),
        typeof(EventToCommandBehavior),
        new PropertyMetadata(default(string)));

    #endregion

    #region CommandParameter

    /// <summary>
    /// CommandParameter Property name
    /// </summary>
    public const string CommandParameterPropertyName = "CommandParameter";

    public object CommandParameter
    {
      get { return (object)GetValue(CommandParameterProperty); }
      set { SetValue(CommandParameterProperty, value); }
    }

    /// <summary>
    /// CommandParameter Property definition
    /// </summary>
    public static readonly DependencyProperty 
        CommandParameterProperty = DependencyProperty.Register(
        CommandParameterPropertyName,
        typeof(object),
        typeof(EventToCommandBehavior),
        new PropertyMetadata(default(object)));

    #endregion
  }
}

All very boring, all very standard. Now, for dynamically adding a listener to the event: I’ve been down this road before, say hello to our friend Observable.FromEventPattern, which takes away all the boring details about the differences between ordinary and WinRT events.

protected override void OnAttached()
{
  var evt = AssociatedObject.GetType().GetRuntimeEvent(Event);
  if (evt != null)
  {
    Observable.FromEventPattern<RoutedEventArgs>(AssociatedObject, Event)
      .Subscribe(se => FireCommand());
  }
  base.OnAttached();
}

Nothing special. Try to find an event with the same name as the contents of the “Event” property and add a listener to it. And the final piece of the puzzle is the actual implementation of FireCommand. Of course, I could have implemented this inline but I like to make this a separate method, if only for readability:

private void FireCommand()
{
  var dataContext = AssociatedObject.DataContext;
  if (dataContext != null)
  {
    var dcType = dataContext.GetType();
    var commandGetter = dcType.GetRuntimeMethod("get_" + Command, new Type[0]);
    if (commandGetter != null)
    {
      var command = commandGetter.Invoke(dataContext, null) as ICommand;
      if (command != null)
      {
        command.Execute(CommandParameter);
      }
    }
  }
}

As always, when you know how to do it, it’s very simple. This method gets the AssociatedObject’s data context, the tries to find the getter of the Command property on this data context. As you might remember, a command in MVVMLight is defined like this:

public ICommand DoSomethingCommand
{
  get
  {
    return new RelayCommand<string>((p) =>
        {
          System.Diagnostics.Debug.WriteLine("Hi there {0}", p);
        });
  }
}

so I am not looking for a command method, but for a getter returning a command. If the code finds that, it tries to Invoke the getter as an ICommand. If it gets that it has the actual command, and it executes it. And that’s all!

So if you want to use this behavior, attach it to a Page’s main grid, for instance:

<Grid Style="{StaticResource LayoutRootStyle}" x:Name="TopView" x:Uid="TopGrid">
   <WinRtBehaviors:Interaction.Behaviors>
    <Win8nl_Behavior:EventToCommandBehavior Event="Tapped"
Command="DoSomethingCommand"
CommandParameter="John doe"/> </WinRtBehaviors:Interaction.Behaviors> <!--- stuff in here --> </Grid>

The top of your page needs to include this name space definitions:

xmlns:WinRtBehaviors="using:WinRtBehaviors"
xmlns:Win8nl_Behavior="using:Win8nl.Behaviors"

and then it will work. Tap on the main grid and you will see the text “Hi there John doe” in the Visual Studio output window. Of course you can make it do more useful things too ;-) and you can also bind things to the CommandParameter property using the normal binding syntax. To a certain extent.

You can download the behavior’s code here, but it's actually a lot easier to just use the NuGet Package. The behavior is there, along with some more nice stuff, and it takes care of downloading WinRtBehaviors and Reactive Extensions for WinRT as well

By special request I've also created sample code - not one but two solutions. The first one, TestWin8nl, is a very simple application that allows you to tap a TextBlock - this will trigger a command in the ViewModel that uses Debug.WriteLine to print a property of the ViewModel in your output console windows, using the CommandParameter to transfer the bound object (the ViewModel itself) to the command. The seconds sample, EventToCommandListDemo, will show you how to handle a SelectionChanged event of a ListBox, although frankly I'd rather just bind SelectedItem and act on it's property setter. But the audience asks, the audiences gets ;-). It appears tough as if Element name binding does not work properly in WinRtBehaviors, so I had to use a ViewModel property to store the selected item in anyway. Both are some contrived examples, but I hope the will do the trick of helping you out understanding how things are working and are supposed to be used.

Update: I have also blogged (and published in win8nl) a variant of the behavior that takes a bound command in stead of a command name. I personally now think this is a better solution as it adheres more to the way regular behaviors work, and is much more flexible Update 2: both this behavior and it's the bound-command-behavior are in the win8nl nuget package. Don't type, be lazy, use Nuget ;-)

23 July 2012

Using a provisional WinRT port of SilverlightSerializer to store state in MVVMLight ViewModels

Over 1.5 years ago I showed how to store your Windows Phone application state (‘tombstoning’) using SilverlightSerializer by Mike Talbot. In my quest to leverage hard-won Windows Phones skills to be usable in Windows 8 I made a provisional port of SilverlightSerializer 1 to WinRT. That is, in C#.

Since my win8nl library port is a bit behind, I’ve stuck the class in a simple project that you can download here.

Usage is as follows: in App.xaml.cs, you define two methods: SaveState and RestoreState:

private async void SaveState()
{
  var file = 
    await ApplicationData.Current.LocalFolder.CreateFileAsync(
      "AppState.dat", CreationCollisionOption.ReplaceExisting);

  using (var fileStream = await file.OpenStreamForWriteAsync())
  {
    SilverlightSerializer.Serialize(MainViewModel.Instance, fileStream);
  }
}

Pretty simple: when the state needs to be saved, create a file, get a stream, and let SilverlightSerializer do its magic. As for RestoreState:

private async Task RestoreState()
{
  try
  {
    var files = await ApplicationData.Current.LocalFolder.GetFilesAsync();
    var dataFile = files.FirstOrDefault(p => p.Name == "AppState.dat");
    if (dataFile != null)
    {
      using (var fileStream = await dataFile.OpenSequentialReadAsync())
      {
        MainViewModel.Instance = 
          SilverlightSerializer.Deserialize(fileStream.AsStreamForRead()) 
            as MainViewModel;
      }
    }
  }
  finally
  {
    if (MainViewModel.Instance == null) MainViewModel.CreateNew();
    MainViewModel.Instance.Start();
  }
}

This basically tries to deserialize a viewmodel from a file “AppState.dat”. If that fails, it creates a new MainViewModel, and starts it. The whole idea behind this is described in the original Windows Phone 7 post, so I encourage you to read that if you have no idea what I am doing here.

Now the important bit is when to call this methods. If you think the most logical place for RestoreState is in OnLaunched, you are right. The first few lines of my OnLaunced method in App.xaml.cs look like this in my app:

if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
  Window.Current.Activate();
  return;
}
else
{
  await RestoreState();
}

Now the most logical place to call SaveState is of course OnSuspending, and we are done, right?

Wrong.

To my own utter surprise, yesterday, after a long debugging session, I found a weird edge scenario. I asserted the following: if a user closes an app by the “Swipe Down gesture”, it takes about 10 seconds (on my Samsung Slate 7) before OnSuspending is called. When the user restarts the app within those 10 seconds from the start screen, the state is not stored yet, or too late – in any case it is not yet available to the (yet again) starting app. So you don’t get the last state, but the state before that, as I shamefully discovered when I proudly showed my state save method to an Application Excellence Lab Microsoftie :(

Don’t get me wrong – the actual time SaveState runs in my app is about 0.047 seconds. It’s lightning fast – just like the original SilverlightSerializer. But the time between user closing the app and the actual state save being fired up was killing my state.

The solution to this is very simple: when the user closes the app, Window.Current.VisibilityChanged is fired. Not like after a few seconds, but instantly. So add and event listener to that at the bottom of OnLaunched, that calls SaveState , like this:

 Window.Current.VisibilityChanged += (o, e) => {if (!e.Visible) SaveState();};

Basically: if the visibility of the current windows changes to invisible, write the state. Maybe you write app state too often (for example because the user temporarily puts it in the background, then VisibilityChanged is fired as well) but at least you circumvent the problem I ran into. Since state writing takes very little time, better often than not at all, I’d say.

And in OnSuspending? Well, you could call SaveState from there, too. That’s useful when your app does something in the background and it’s state changes even when the users has put it in the background. Then, when the OS kicks it into suspension because it runs out of memory, the state is saved too.

Caveat emptor: I kinda roughly hacked Mike Talbots work to operate in the WinRT world. The current state is ‘works for me’. I commented out the whole section about Dictionary support, so I suppose that does not work. My ultimate goal is to convert SilverlightSerializer 2, but that was too big a bone to chew in a short time. It serves my ported game Catch’em Birds fine. I hope to get it finished soon.

20 June 2012

Reflection in WinRT: use Rx Extensions for dynamically adding event handlers

Suppose you want to add an event handler, but you don’t know up front which event. Admittedly, this is a bit of an edge case, but I have used it in a behavior that starts a storyboard upon an event (I will publish that later). The designer can specify the event using a string value in XAML, so I cannot simply use object.MyEvent += (handler). We need reflection here.

So suppose you have found the event in the object using the GetRuntimeXXX methods like I described earlier today, and want to dynamically add a handler to the event “MyEvent”:

var evt = MyObject.GetType().GetRuntimeEvent("MyEvent");
evt.AddEventHandler(MyObject, 
  new EventHandler((p, q) => {// your code here }));

This will compile, and even run - unless the object is, for instance, a Grid, and the event “Loaded”. That’s apparently a “WinRT event”, whatever that may be, and this will result in a runtime exception:

"Adding or removing event handlers dynamically is not supported on WinRT events."

Googling Binging this message leads to all kind of complicated solutions using the WindowsRuntimeMarshall class (without any samples, alas), but the solution turns out to be very simple: use Rx Extensions Beta for Windows RT. For the really lazy reader: click tools/Library Package Manager/Package Manager Console and enter “Install-Package Rx-Main –Pre” in the Package Manager Console.

Now add

using System.Reactive.Linq;

to the top of the code file and then simply use the following code:

Observable.FromEventPattern<RoutedEventArgs>(MyObject, "MyEvent")
  .Subscribe(se => { // your code here });

and you are done. The difference between ordinary events and “WinRT events” is apparently swallowed up in the bowels of Rx. This library is great to begin with, but if it saves you the trouble of digging in yet another still not very thoroughly documented API, it’s even better. As a bonus, by very nature of Rx the handler is automatically disposed of when your calling object goes out of scope, which is not necessarily the case in my first code. But that did not work anyway ;-)

Reflection in WinRT: DeclaredProperties vs GetRuntimeProperties caveat

I suppose this is part of some FM I did not R, but I would like to point it out in case some other sod wastes just as much time on it as me.

I embarked upon a journey to port SilverlightSerializer and some behaviors to WinRT. All stuff is heavily making use of reflection. Now reflection has been hugely overhauled in WinRT, so I knew I was in for an interesting ride. Recently I was convinced I managed to do it, but it turned out I did not.

There are apparently two ways to use reflection in RT. What I did use for reading all properties of an object, was:

var properties = MyObject.GetType().GetTypeInfo().DeclaredProperties;

But it turns out this returns only the properties declared in the class itself. What it does not return, are the properties of possible parent classes!

What I should have used, apparently, is:

var allproperties = MyObject.GetType().GetRuntimeProperties();

When I feed a Grid into “MyObject”,  “properties” hold 6 properties, while “allproperties” holds 51.

I don’t know why this distinction is made, and I understand even less how come I missed this the first time. On my first meeting as an MVP, Dennis Vroegop, after welcoming me to the club, already warned me that the MVP award did not automatically come with super powers. Boy, was he right.

14 June 2012

NavigationService for WinRT

"If I have been able to see further than others, it is because I have stood on the shoulders of giants."
 Isaac Newton

It was only 1.5 years ago, but it seems already a long time ago that Laurent Bugnion described a “view service” for Windows Phone navigation, commonly know as the NavigationService. I’ve incorporated this code in my #wp7nl library on codeplex and have been using it happily ever since (and a lot of other people I know have been doing so as well), Time goes on, along came Windows 8 and WinRT and Metro Style Apps.

Laurent ported his MVVMLight framework to WinRT as well. But the NavigationService never has been a core part of MVVMLight (therefore I put it in the #wp7nl library) and porting it to WinRT as well proved to be a bit of a hassle. Some of its premises where no longer valid – most notable the way to retrieve the main application frame by accessing Application.Current.RootVisual, which does not work in WinRT. I messed around a little with the code, did not get anywhere, and left it there. So I was glad I saw my fellow Windows Phone Development MVP Matteo Pagani tweet he got a NavigationService to work. He was kind enough to mail me the code. He basically copied the code from Windows Phone and made the apps rootFrame, as created in the App.xaml.cs, publicly available. A neat trick, with as only drawback that you have to copy the code to every app. As things goes, it’s usually easier to see someone else’s idea and improve it, than think it up out of the blue. I toyed around with it and managed to get rid of the need to copy the code, so I could put it in a library.

And here it is, an improved version of a NavigationService for WinRT, based upon Matteo’s code based upon Laurent’s code ;-)

First of all, the NavigationService’s interface:

using System;
using Windows.UI.Xaml.Navigation;

namespace Win8nl.Services
{
  public interface INavigationService
  {
    event NavigatingCancelEventHandler Navigating;
    void Navigate(Type type);
    void Navigate(Type type, object parameter);
    void Navigate(string type);
    void Navigate(string type, object parameter);
    void GoBack();
  }
}

This quite looks like the old interface, with some notable exceptions. First of all, the NavigateTo method is renamed Navigate, to mimic the method name used in WinRT itself. And it has not one but four overloads. The first two are pretty logical – WinRT does not use an uri for navigation, but an actual Type object, making strongly-typed navigation possible. And it supports an overload for a parameter object, as well. The two other methods, with the string type object… well see for yourself below, in the implementation of the service:

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace Win8nl.Services
{
  public class NavigationService : INavigationService
  {
  public NavigationService(Frame mainFrame)
  {
    _mainFrame = mainFrame;
  }

  private Frame _mainFrame;
  
  public event NavigatingCancelEventHandler Navigating;

  public void Navigate(Type type)
  {
    _mainFrame.Navigate(type);
  }

  public void Navigate(Type type, object parameter)
  {
    _mainFrame.Navigate(type, parameter);
  }

  public void Navigate(string type, object parameter)
  {
    _mainFrame.Navigate(Type.GetType(type), parameter);
  }

  public void Navigate(string type)
  {
    _mainFrame.Navigate(Type.GetType(type));
  }

  public void GoBack()
  {
    if (_mainFrame.CanGoBack)
    {
      _mainFrame.GoBack();
    }
  }  
}

So the string type methods allow you to specify the page to navigate by string. Before you all think I really lost my marbles this time, re-introducing weakly typed navigation just as the Windows team introduced strongly typed: I specifically added this as to allow navigation to be initiated from a ViewModel contained in an assembly not containing the XAML code. This also enables you to test code as well. This is the way I built my solutions for Windows Phone, and I intend to go on this way in Windows 8. ;-)

Anyway, there’s a thing missing in this code: the EnsureMainFrame that looked up the root page, for which Matteo used a public property. I completely removed it, and added a constructor accepting that root page. By creating that constructor I deleted the default constructor, so registering the NavigationService at he SimpleIoc container shipped with MVVMLight in the App.xaml.cs, like this:

SimpleIoc.Default.Register<INavigationService, Wp7nl.Utilities.NavigationService>();

as we did in Windows Phone code, won’t work anymore. Fortunately, SimpleIoc also supports factory methods to create an instance. So now you go to the App.xaml.cs of your Windows 8 app, find the OnLaunched method and just behind this line:

var rootFrame = new Frame();

you add this piece of code:

SimpleIoc.Default.Register<INavigationService>(() => 
                                   {return new NavigationService(rootFrame);});

Now if your XAML code and ViewModels are all in one assembly, you can call the NavigationService to navigate to the main page this:

SimpleIoc.Default.GetInstance<INavigationService>().Navigate(typeof(MainPage));

or if you are a bit stubborn like me and would like to separate ViewModels and views, you can use the string overload to specify the full qualified name and for instance use it in a ViewModel command like this

public ICommand TestNavigatieCommand
{
   get
   {
     return new RelayCommand(() => 
       SimpleIoc.Default.GetInstance<INavigationService>().Navigate("MyApp.MainPage,MyApp"));
   }
}

And there you have, a fully functional NavigationService for Windows 8. Code can be downloaded here. I will soon incorporate it in the provisional port of the #wp7nl library called win8nl.

Thanks to Laurent and Matteo for being my trailblazers ;-)

Behavior to make Bing Maps view area bindable

The Bing Maps control in Windows Phone 7 is pretty versatile but does not always play along very well with data binding. You can bind ZoomLevel and MapCenter, which is all very well, but if you want to know what area is actually shown within the map – for instance, for making a GIS application that loads only visible data, and not stuff that’s outside the view area anyway, things get a lit complicated.

Not so if you uses this little behavior. It builds upon the SafeBehavior that is already in my wp7nl library on codeplex. It sports two GeoCoordinate depencency propertise, NorthEast and SouthWest

using System.Device.Location;
using System.Windows;
using Microsoft.Phone.Controls.Maps;

namespace Wp7nl.Behaviors
{
  public class ViewportAreaBehavior
  {
    /// <summary>
    /// A behavior to make southwest and northeast bindable
    /// </summary>
    public class ViewportwatcherBehavior : SafeBehavior<Map>
    {
      #region NorthWest
      public const string NorthWestPropertyName = "NorthWest";

      public GeoCoordinate NorthWest
      {
        get { return (GeoCoordinate)GetValue(NorthWestProperty); }
        set { SetValue(NorthWestProperty, value); }
      }

      public static readonly DependencyProperty NorthWestProperty = 
	DependencyProperty.Register(
          NorthWestPropertyName,
          typeof(GeoCoordinate),
          typeof(ViewportwatcherBehavior),
          new PropertyMetadata(null));

      #endregion

      #region SouthEast
      public const string SouthEastPropertyName = "SouthEast";

      public GeoCoordinate SouthEast
      {
        get { return (GeoCoordinate)GetValue(SouthEastProperty); }
        set { SetValue(SouthEastProperty, value); }
      }

      public static readonly DependencyProperty SouthEastProperty = 
	DependencyProperty.Register(
          SouthEastPropertyName,
          typeof(GeoCoordinate),
          typeof(ViewportwatcherBehavior),
          new PropertyMetadata(null));

      #endregion
    }
  }
}

All pretty standard stuff. Next comes the setup/cleanup stuff,

protected override void OnSetup()
{
  AssociatedObject.ViewChangeEnd += AssociatedObjectViewChangeEnd;
  CalcRectangle();
}

protected override void OnCleanup()
{
  AssociatedObject.ViewChangeEnd -= AssociatedObjectViewChangeEnd;
}

in which you can see I intercept the ViewChangeEnd event. I don’t do anything special there: I just call the method CalcRectangle, just as on startup.

void AssociatedObjectViewChangeEnd(object sender, MapEventArgs e)
{
  CalcRectangle();
}

The final missing piece of the puzzle is then of course CalcRectangle itself:

private void CalcRectangle()
{
  NorthWest = AssociatedObject.ViewportPointToLocation(new Point(0, 0));
  SouthEast = AssociatedObject.ViewportPointToLocation(
    new Point(AssociatedObject.ViewportSize.Width, 
              AssociatedObject.ViewportSize.Height));
}

So I basically take the point left top (0,0) and and use ViewportPointToLocation to convert it to a coordinate, and do the same think with the point that left bottom. And that’s all.

Now you can bind to the NorthWest and SouthEast property of the behavior and you get the view area in your view model. Be aware this works one way. It gets the view area to the ViewModel, not the other way around.

Contrary to my custom there’s no solution with the code in it, mostly because it’s already in the wp7nl library since yesterday. You can download the code from the library itself here, if you want.