06 May 2012

JSON deserialization with JSON.net: caching results

On January 22 I promised this to be a three-part series. I’ve been kinda busy with upgrading apps, Windows 8 experiments and trivial ;-) stuff like code camps, an MVP summit, preparing my first and second talk about Windows Phone and whatnot and made you wait for the final part for exactly three months – but those who know me, know I stick my promises, so here’s the third and final part of my JSON for Windows Phone series.

In part 1 of this series I described the basics of creating classes from a JSON string and then simply deserializing the string into a (list of) classes. In part 2 I showed how to use JSONConverter subclasses to handle complex stuff the deserializer cannot handle out of the box, like class hierarchies. Part 3, as promised, shows a way to cache results - which makes your application faster, more responsive and more battery/data plan friendly.

Using the demo solution of part 2 as a starting point, I first brought in my wp7nl library on codeplex using NuGet. I am lazy just like any programmer (should be) and I like to defer as much heavy lifting to already existing code as I can ;-).

When dealing with data downloaded from the web everything becomes asynchronous by nature (at least on Windows Phone), and since we don’t have the await and async keyboards aboard our platform yet, I tend to use Observables from Microsoft.Phone.Reactive. To let that work with events, I need an EventArgs child class for my “loading completed” delegate, which I have defined in the following trivial way:

using System;
using System.Collections.Generic;

namespace Wp7nl.Utilities
{
  public class DataLoadCompletedArgs<T> : EventArgs
  {
    public IList<T> Result { get; set; }

    public Exception Error { get; set; }
  }
}

It’s a generic class because I am basically too lazy to write casting statements everywhere. The basic setup of the helper class that does both loading itself is like this:

using System.Linq;
using Microsoft.Phone.Reactive;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using Newtonsoft.Json;

namespace Wp7nl.Utilities
{
  public class CachedServiceDataLoader<T> where T : class
  {
    private readonly IsolatedStorageHelper<List<T>> storageHelper;

    public CachedServiceDataLoader()
    {
      storageHelper = new IsolatedStorageHelper<List<T>>();
    }

    private void FireDataLoadCompleted(IList<T> result, Exception error)
    {
      if (DataLoadCompleted != null)
      {
        DataLoadCompleted(this,
                          new DataLoadCompletedArgs<T> 
                          {Result = result, Error = error});
      }
    }

    public delegate void DataLoadCompletedHandler(object sender, 
      DataLoadCompletedArgs<T> args);

    public event DataLoadCompletedHandler DataLoadCompleted;
  }
}

So what have we here? A constructor that creates an IsolatedStorageHelper – that’s a class from the latest version from Wp7nl, where it sits in the Wp7nl.Utilities namespace. It’s basically the internal logic of the extension methods I described in my article about tombstoning MVVMLight viewmodels using SilverlightSerializer cut loose from those extension methods – so they can be used for caching all kinds of classes, and not only viewmodels on deactivation or closing of the app. The bottom part of the class is just the event indicating a data load action has been completed, and a convenience method to easily fire that event (I mentioned I was lazy, didn’t I? ;-) ) .

Next up is another little convenience methods for loading stuff from the cache or returning a default (empty) list if the results are not available:

private List<T> LoadFromStorage()
{
  return storageHelper.ExistsInStorage() ? 
     storageHelper.RetrieveFromStorage() : new List<T>();
}

It’s only used once, so I could just a easy have omitted it, but it makes the rest of the code a bit more readable and that’s important too. When you state your intentions in code, that saves on comment.

The method to read stuff from cache is a bit more complex than strictly necessary, but since data coming from the web is coming in asynchronously, why would I want my app to wait on my cache coughing up its results? So I made the cache retrieval asynchronously as well, using some old skool BackgroundWorker hoopla:

public bool StartLoadFromCache()
{
  if (storageHelper.ExistsInStorage())
  {
    var w = new BackgroundWorker();
    w.DoWork += (s, e) =>
      {
        e.Result = LoadFromStorage();
      };

    w.RunWorkerCompleted += (s, e) =>
      FireDataLoadCompleted(e.Result as List<T>, e.Error);

    w.RunWorkerAsync();
    return true;
  }
  return false;
}

I could have used an observable here as well I guess, but I still have not adapted my code snippet. So anyway, I cheat a little by checking first if there’s any cache data at all – if there is not, the method immediately returns false, informing the calling method there’s no cached data and to go get the stuff on the web. Which it can, incidentally, by calling the following method:

public void StartDownloadCacheData(IList<T> currentObjects, Uri serviceUri,
                                   params JsonConverter[] converters)
{
  var w = new SharpGIS.GZipWebClient();
  Observable.FromEvent<DownloadStringCompletedEventArgs>(
    w, "DownloadStringCompleted")
    .Subscribe(r =>
    {
      if (DataLoadCompleted != null)
      {
        if (r.EventArgs.Error == null)
        {
          var deserialized = 
               JsonConvert.DeserializeObject<List<T>>(r.EventArgs.Result,
                                                      converters);
          var result = new List<T>(currentObjects);
          result.AddRange(deserialized.Where(p => !currentObjects.Contains(p)));
          FireDataLoadCompleted(result, r.EventArgs.Error);
          storageHelper.SaveToStorage(result);
        }
        else
        {
          FireDataLoadCompleted(null, r.EventArgs.Error);
        }
      }
    });

  w.DownloadStringAsync(serviceUri);
}

So what this method does is pretty simple:

  • It makes a GZipWebClient (that allows data to be loaded zipped, thus saving on amount of bytes actually transmitted)
  • It makes an Observable from the event “DownloadStringCompleted” and subscribes an anonymous method
  • It fires the DownloadStringAsync method on the uri

Now the anonymous method that is fired when the data arrives

  • Checks for errors
  • Tries to deserialize the incoming data using the provides converters (if any)
  • Adds the existing object list to the result list
  • Add the new objects to the list after weeding out duplicates that were already in the list
  • Fires the complete event
  • Stores the now merged data set in isolated storage.

So the idea is that you can do consecutive downloads, but that resulting data never contains any duplicates. This is why you must provide the list of current objects. This may seem like a little odd, but this is exactly the thing you want to do with working with geographical data – which I do almost all the time for a living. As an inspector you want to download stuff from the areas you want to inspect, not the whole municipality. You move your map to a location, hit “download” and presto, you have the stuff you want to use cached on you device before you go on the road. Repeat until all areas you want to visit today have been processed. Kinda like Nokia Drive does – there is never enough room aboard your phone to store all the possible maps of the world, but when you to Seattle, you download the maps for Washington State, not the whole of North America, and you’re good to go.

But what if you want to start over – clearing the cache in stead of adding it? That’s the last method of this class:

public void StartClearStorage()
{
  var w = new BackgroundWorker();
  w.DoWork += (s, e) => storageHelper.DeletedFromStorage();

  w.RunWorkerCompleted += (s, e) =>
  {
    if (DataLoadCompleted != null)
    {
      FireDataLoadCompleted(new List<T>(), e.Error);
    }
  };

  w.RunWorkerAsync();
}

Also asynchronously, also not strictly necessary, but Microsoft tend to move toward doing everything asynchronously – if you have done any Windows 8 development you know performance requirements are pretty high and strict, so better get used to it already.

JSONdemo3I updated the demo solution to show off the workings of this class, that presents itself like showed to the right. If you click on “Load devices”, it will show four devices, as displayed on the image, and the message “Loading from web”. If you hit the “Load devices” button again, it will show the same four devices, but show the message “Loading from cache” and it will show them just a wee bit faster.

If you hit “Load more devices” you will see six devices, – with the Lumia 800 mentioned two times. But wait, what about weeding out the duplicates in StartDownloadCacheData – doesn’t that work? It sure does, but I already mentioned I was lazy, so I did not implement any “Equals” logic on  the Device class – that’s left as exercise for the reader ;-). I assure you it will work properly then.

“Clear list” just clears the list, and if you hit “Load devices” it will load the devices from cache again – very fast.

“Clear cache and list” will actually wipe the cache, so if you hit “Load devices” again it will show “Loading from web”.

I won’t bother you with the XAML – the code in MainPage.xaml.cs is pretty straightforward and shows off all the features of the CachedServiceDataLoader:

using System;
using System.Collections.Generic;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Reactive;
using Wp7nl.Utilities;

namespace JsonDemo
{
  public partial class MainPage : PhoneApplicationPage
  {
    private CachedServiceDataLoader<Device> cachedLoader;
    private IList<Device> currentData;
    public MainPage()
    {
      InitializeComponent();
      Loaded += MainPage_Loaded;
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
      cachedLoader = new CachedServiceDataLoader<Device>();
      currentData = new List<Device>();
      Observable.FromEvent<DataLoadCompletedArgs<Device>>(
        cachedLoader, "DataLoadCompleted")
       .Subscribe(r =>
                    {
                      currentData = r.EventArgs.Result;
                      PhoneList.ItemsSource = r.EventArgs.Result;
                    });
    }

    private void Load_Click(object sender, RoutedEventArgs e)
    {
      if (!cachedLoader.StartLoadFromCache())
      {
        Message.Text = "Loading from web";
        cachedLoader.StartDownloadCacheData(currentData,
          new Uri("http://www.schaikweb.net/dotnetbyexample/JSONPhones2.txt"),
          new JsonDeviceConverter(), new JsonSpecsConverter());
      }
      else
      {
        Message.Text = "Loading from cache";        
      }
    }

    private void Load2_Click(object sender, RoutedEventArgs e)
    {
      Message.Text = "Loading from web";
      cachedLoader.StartDownloadCacheData(currentData,
        new Uri("http://www.schaikweb.net/dotnetbyexample/JSONPhones3.txt"),
        new JsonDeviceConverter(), new JsonSpecsConverter());
    }      

    private void Clear_Click(object sender, RoutedEventArgs e)
    {
      PhoneList.ItemsSource = null;
      Message.Text = "cleared list (not cache)";
    }

    private void ClearCache_Click(object sender, RoutedEventArgs e)
    {
      cachedLoader.StartClearStorage();
      Message.Text = "cleared list and cache";
    }
  }
}

There isn’t even any of my trademark MVVM code in here. In the MainPage_Loaded – not surprisingly - all the stuff is initialized. The CachedServiceDataLoader is created using a type T, and spits out a IList of T. I use an Observable to keep track of things here as well, but of course you are free to subscribe to events in the ‘old fashioned’ way.

Load_Click shows the usage of StartLoadFromCache and StartDownloadCacheData, the latter one using the JSONconverter child classes I showed in part 2. The rest I assume to be pretty straightforward.

As you can see, working with JSON on Windows Phone is dead easy and my little helper class makes it even more easy. I am still pondering if I should include this in the wp7nl library, as this creates yet two more dependencies (SharpGIS.GZipWebClient and Newtonsoft.Json) to keep in sync. If you have any feedback on this, I’d be happy to hear it. But anyway, this concludes my JSON for Windows Phone series, I hope it will prove to be a useful mini-tutorial for the Windows Phone developer community. And as usual, you can download the full solution from my website.

01 April 2012

Porting the DragFlickBehavior from Windows Phone 7 to Windows 8 Metro Style

Preface

A little over a year ago I made DragFlickBehavior, a behavior for Windows Phone that makes essentially anything draggable and ‘flickable’, that is, you can drag a GUI element along with your finger and it seems to have a little inertia when you let it go. In my previous post, I described the basics of how to make a behavior at all for Windows 8 Metro style. The testing of this was done using a ported version of the DragFlickBehavior. I’ve retraced my steps to how I got it to work, and will describe the process of porting an existing behavior here.

For the DragFlickBehavior to work, some groundwork needed to be layed first. For Windows Phone, I made a couple of extension methods for both FrameworkElement and StoryBoard first. To make matters worse, one of those extension methods in FrameworkElementExtensions used yet another extension method – GetVisualParent in VisualTreeHelperExtensions from Phone7.Fx… Nil desperandum… I’ll start at the beginning

Porting  VisualTreeHelperExtensions

I created a class library Win8nl.External, copied VisualTreeHelperExtensions.cs from it’s codeplex location, and opened the it the editor. And then the process was pretty simple:

  • The namespace System.Windows.Media is gone. So I deleted it’s using.
  • I basically clicked every red line, hit Control-.  (that’s Control-dot) and in most cases the editor would suggest a name space to add

In the end I seemed to have added

using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;

And then there was this slight matter of 2 places where the author calls VisualStateManager.GetVisualStateGroups and expects the result to be an IList. Now it’s an IEnumerable. Anyway, I solved this by changing the

IList groups = VisualStateManager.GetVisualStateGroups(root);

into

var groups = VisualStateManager.GetVisualStateGroups(root);

on both occasions. One file done. I won’t even pretend I understand what all those methods in this file are actually doing. I just ported them.

Porting FrameworkElementExtensions

I then created a library Win8nl, added references to Win8nl.External and WinRtBehaviors, and started on the FrameworkElementExtensions . This proved to be a pretty trivial matter. I needed to remove

using System.Windows.Controls;
using System.Windows.Media;
using System.Linq;
using Phone7.Fx;

And add after Control-dotting trough the errors I found I had added

using Windows.UI.Xaml.Media;
using Windows.UI.Xaml;
using Windows.Foundation
using Windows.UI.Xaml.Controls

Two files done!

Porting StoryboardExtensions

Routine starts to settle in. Remove

using System.Windows.Media;
using System.Windows.Media.Animation;
Control-dot around, and you will see you've added
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;

But then we hit our first snag. Two methods use a parameter of type IEasingFunction, that does no longer exist. But that can be fixed, by changing it into EasingFunctionBase.

Then I found out that Storyboard.SetTargetProperty apparently no longer wants to have a PropertyPath – which can be made from a DependencyProperty object – but a string. So method

public static void AddAnimation(this Storyboard storyboard,
 DependencyObject item, Timeline t, DependencyProperty p)
{
  if (p == null) throw new ArgumentNullException("p");
  Storyboard.SetTarget(t, item);
  Storyboard.SetTargetProperty(t, new PropertyPath(p));
  storyboard.Children.Add(t);
}
Need to be changed to
 public static void AddAnimation(this Storyboard storyboard,
 DependencyObject item, Timeline t, string property)
{
  if (string.IsNullOrWhiteSpace(property)) throw new ArgumentNullException("property");
  Storyboard.SetTarget(t, item);
  Storyboard.SetTargetProperty(t, property);
  storyboard.Children.Add(t);
}
This is bad news, since it breaks the public interface. And it breaks even more, namely another public extension method
public static void AddTranslationAnimation(this Storyboard storyboard,
   FrameworkElement fe, Point from, Point to, Duration duration,
   EasingFunctionBase easingFunction)
{
  storyboard.AddAnimation(
      fe.RenderTransform,
      storyboard.CreateDoubleAnimation(duration, from.X, to.X, easingFunction),
                                       CompositeTransform.TranslateXProperty);
  storyboard.AddAnimation(fe.RenderTransform,
       storyboard.CreateDoubleAnimation(duration, from.Y, to.Y, easingFunction),
                                        CompositeTransform.TranslateYProperty);
}
Needs to be changed to
public static void AddTranslationAnimation(this Storyboard storyboard,
  FrameworkElement fe, Point from, Point to, Duration duration,
  EasingFunctionBase easingFunction)
{
  storyboard.AddAnimation(fe.RenderTransform,
  storyboard.CreateDoubleAnimation(duration, from.X, to.X, easingFunction),
                                   "TranslateX");
  storyboard.AddAnimation(fe.RenderTransform,
  storyboard.CreateDoubleAnimation(duration, from.Y, to.Y, easingFunction),
                                   "TranslateY");
}

I must honestly say I find the apparent need to specify storyboard target properties verbatim, as in strings, quite peculiar, but apparently this is the way it needs to be done. I am only the messenger here.

Porting DragFlickBehavior

Here we go again. Delete
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Wp7nl.Utilities;
And Control-dotting learns you the following needs to be added:
using Win8nl.Utilities;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using WinRtBehaviors;
Soon after that, you'll learn that the second parameter of a ManipulationDelta event is no longer of type ManipulationDeltaEventArgs but of ManipulationDeltaRoutedEventArgs, and that it does no longer have a “DeltaManipulation” property but a plain “Delta” property. So the AssociatedObjectManipulationDelta method capturing the event was this:
void AssociatedObjectManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
  var dx = e.DeltaManipulation.Translation.X;
  var dy = e.DeltaManipulation.Translation.Y;
  var currentPosition = elementToAnimate.GetTranslatePoint();
  elementToAnimate.SetTranslatePoint(currentPosition.X + dx, currentPosition.Y + dy);
}
and now needs to be this
void AssociatedObjectManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
  var dx = e.Delta.Translation.X;
  var dy = e.Delta.Translation.Y;
  var currentPosition = elementToAnimate.GetTranslatePoint();
  elementToAnimate.SetTranslatePoint(currentPosition.X + dx, currentPosition.Y + dy);
}

No rocket science in there, right? And almost identical set of rework needs to be done to the method capturing ManipulationCompleted. Its second parameter was of type ManipulationCompletedEventArgs and is now – you’ve probably guessed it – ManipulationCompletedRoutedEventArgs. And that does no longer have a property e.FinalVelocities.LinearVelocity.X and Y but is does have a Velocities.Linear.X and Y.

For some reason though, those properties return values that are somewhere between 0 and 1, or at least it seems so. So I made a rule-of-thumb conversion multiplying them by 1000. Wrapping that up: AssociatedObjectManipulationCompleted used to be

private void AssociatedObjectManipulationCompleted(object sender,
                                                    ManipulationCompletedEventArgs e)
{
  // Create a storyboard that will emulate a 'flick'
  var currentPosition = elementToAnimate.GetTranslatePoint();
  var velocity = e.FinalVelocities.LinearVelocity;
  var storyboard = new Storyboard { FillBehavior = FillBehavior.HoldEnd };

  var to = new Point(currentPosition.X + (velocity.X / BrakeSpeed),
                     currentPosition.Y + (velocity.Y / BrakeSpeed));
  storyboard.AddTranslationAnimation(elementToAnimate, currentPosition, to, 
    new Duration(TimeSpan.FromMilliseconds(500)), 
    new CubicEase {EasingMode = EasingMode.EaseOut});
  storyboard.Begin();
}
and it now is
private void AssociatedObjectManipulationCompleted(object sender,
                                                   ManipulationCompletedRoutedEventArgs e)
{
  // Create a storyboard that will emulate a 'flick'
  var currentPosition = elementToAnimate.GetTranslatePoint();
  var xVelocity = e.Velocities.Linear.X * 1000;
  var yVelocity = e.Velocities.Linear.Y * 1000;
  var storyboard = new Storyboard { FillBehavior = FillBehavior.HoldEnd };
  var to = new Point(currentPosition.X + (xVelocity / BrakeSpeed),
                     currentPosition.Y + (yVelocity / BrakeSpeed));
  storyboard.AddTranslationAnimation(elementToAnimate, currentPosition, to,
      new Duration(TimeSpan.FromMilliseconds(500)),
   new CubicEase { EasingMode = EasingMode.EaseOut });
  storyboard.Begin();
}

The final thing you will need to take into consideration when you port behaviors is the fact that you used to have an OnAttached and Loaded event. You still have those, but by the very nature I implemented behaviors everything that happened in OnAttached and OnLoaded needs to be in OnAttached. Same goes for Unloaded and OnDetaching – the last one is fired by the first one. So follow the pattern I set out: initialize in OnAttached only, and only clean up in OnDetached.

So, the behavior used to have a setup like this:

protected override void OnAttached()
{
  base.OnAttached();
  AssociatedObject.Loaded += AssociatedObjectLoaded;
  AssociatedObject.ManipulationDelta += AssociatedObjectManipulationDelta;
  AssociatedObject.ManipulationCompleted += AssociatedObjectManipulationCompleted;
}

void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
{
  elementToAnimate = AssociatedObject.GetElementToAnimate();
  if (!(elementToAnimate.RenderTransform is CompositeTransform))
  {
    elementToAnimate.RenderTransform = new CompositeTransform();
    elementToAnimate.RenderTransformOrigin = new Point(0.5, 0.5);
  }
}
And that should now be 
protected override void OnAttached()
{
  elementToAnimate = AssociatedObject.GetElementToAnimate();
  if (!(elementToAnimate.RenderTransform is CompositeTransform))
  {
    elementToAnimate.RenderTransform = new CompositeTransform();
    elementToAnimate.RenderTransformOrigin = new Point(0.5, 0.5);
  }
  AssociatedObject.ManipulationDelta += AssociatedObjectManipulationDelta;
  AssociatedObject.ManipulationCompleted += AssociatedObjectManipulationCompleted;
  AssociatedObject.ManipulationMode = 
    ManipulationModes.TranslateX | ManipulationModes.TranslateY;
  base.OnAttached();
}
Notice a couple of interesting things:
  • The capture of “Loaded” is gone. We don’t need that any longer
  • There is an extra last line, setting the “ManipulationMode”. Apparently you need to set that up to make ManipulationDelta and ManipulationCompleted happen at all. It accidently stumbled upon that

Finally, the last part: OnDetaching. It used to be

protected override void OnDetaching()
{
  AssociatedObject.Loaded -= AssociatedObjectLoaded;
  AssociatedObject.ManipulationCompleted -= AssociatedObjectManipulationCompleted;
  AssociatedObject.ManipulationDelta -= AssociatedObjectManipulationDelta;

  base.OnDetaching();
}
And the only thing that needs to be changed to use that is the removal of the first line: AssociatedObject.Loaded -= AssociatedObjectLoaded;;

And then we’re done. If you add this behavior to any object on the screen, like I showed in the previous post:

<Page
  x:Class="Catchit8.BlankPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:Catchit8"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:Win8nl_Behaviors="using:Win8nl.Behaviors"
  xmlns:WinRtBehaviors="using:WinRtBehaviors"
  mc:Ignorable="d">

  <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
    <TextBlock HorizontalAlignment="Left" Margin="503,213,0,0" TextWrapping="Wrap" 
   VerticalAlignment="Top" FontSize="18" Text="Drag me">
      <WinRtBehaviors:Interaction.Behaviors>
         <Win8nl_Behaviors:DragFlickBehavior BrakeSpeed ="5"/>
      </WinRtBehaviors:Interaction.Behaviors>
    </TextBlock>
    <Button Content="Drag me too!" HorizontalAlignment="Left" Margin="315,269,0,0" 
   VerticalAlignment="Top" >
      <WinRtBehaviors:Interaction.Behaviors>
          <Win8nl_Behaviors:DragFlickBehavior BrakeSpeed ="5"/>
      </WinRtBehaviors:Interaction.Behaviors>
     </Button>

  </Grid>
</Page>

You will get an effect like this (I added a slider just for kicks)

DragFlickBehavior on Windows 8 demonstrated

Conclusion

At first glance, Windows 8 development does not seem to differ that much from Windows Phone development. After I made my behavior framework, porting a fairly complex behavior like this was pretty easy, so I’d say that holds true at second glance as well. Sure, some things are different – mostly namespaces and some property names. The XAML is a wee bit different as well. As to why Microsoft have decided to change namespaces, rename properties or methods or even let return values be a bit different – I don’t know. What I do know is that bitching about it will probably raise your blood pressure but it won’t help you very much as a developer. Just think of this: Microsoft sold 450 million copies of Windows 7. I don’t think those will all be Windows 8 next year, but I think the 100 million mark will be hit pretty soon. The choice is yours – either you are spending time and energy on getting angry that Microsoft moved your cheese (or actually, only some of it) or you can go out and find new and probably a bloody lot of cheese.

Well, I’ve made my choice

As usual, a complete demo solution for those who, like me, are too lazy to do all the typing themselves, can be found here. So you can get started even faster. ¡Arriba! ¡Andale! ;-)

29 March 2012

Attached behaviors for Windows 8 Metro Style XAML

This post was updated substantially at March 31, 2012

Regular readers of my blog know that there are some recurring themes: MVVM, maps and behaviors. I am a big fan of using behaviors ever since I learned how to use this rooting trough the sources of MVVMLight. When I saw the BUILD videos I was elated. I saw a great merger of the best things of Windows and  Windows Phone 7 styles and I knew I was going to get on board too. Five months later, I found myself being an MVP and on the Microsoft Campus of all places, and got a pretty unpleasant surprise: a lady presenting the new Expression Blend version said there would be no behaviors in Windows 8 Metro Style XAML. I was quite disappointed at the time. I still think it’s is quite an omission, but then again, when it’s not your deadline it’s always easy to criticize others.

And then for some reason, this week, I remembered a single line from a presentation by Laurent Bugnion on the 2012 Microsoft Techdays in The Hague. “You can’t use behaviors but you can use attached dependency properties”. It kept reverbing trough my brain for a few moments.

“Use the Force, Luke” ;-)

And the result is this. It’s crude, it’s clumsy, it has no Blend support, but it works, more or less – I have been able to port my DragFlickBehavior to Windows 8 and it bloody works, too. This blog post will be split in two parts: in this part, I will show how to make a behavior in Windows 8 XAML in general, and in a next one I will specifically show the DragFlickBehavior itself.

First of all, the behavior class itself:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;

namespace WinRtBehaviors
{
  public abstract class Behavior : DependencyObject
  {
    private FrameworkElement associatedObject;
    public FrameworkElement AssociatedObject
    {
      get
      {
        return associatedObject;
      }
      set
      {
        if (associatedObject != null)
        {
          OnDetaching();
        }
        associatedObject = value;
        if (associatedObject != null)
        {
          OnAttached();
        }
      }
    }

    protected virtual void OnAttached()
    {
      AssociatedObject.Unloaded += AssociatedObjectUnloaded;
    }

    protected virtual void OnDetaching()
    {
      AssociatedObject.Unloaded -= AssociatedObjectUnloaded;
    }

    void AssociatedObjectUnloaded(object sender, RoutedEventArgs e)
    {
      OnDetaching();
    }
  }
}

This is partially ‘borrowed’ from the Windows Phone System.Windows.Interactivity.dll, courtesy of Reflector.  I don’t have a real ‘detached’ event so I’ve decided to call the “OnDetaching’ method when the FrameworkElement is unloaded. Gotta use what’s available, right? The next class, which is the typed version of AttachedBehavior, is also courtesy of Reflector:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;

namespace WinRtBehaviors
{
  public abstract class Behavior<T> : Behavior 
    where T : FrameworkElement
  {
    protected Behavior()
    {
    }

    public T AssociatedObject
    {
      get
      {
        return (T)base.AssociatedObject;
      }
      set
      {
        base.AssociatedObject = value;
      }
    }
  }
}

I’ve closely followed naming conventions as used in Windows Phone and Silverlight, but I took a different root namespace “WinRtBehaviors”. Should the Windows 8 team decide to add behaviors to the API in the future, removing this classes and changing the namespaces should do the trick

Finally there is this pretty crazy piece of code, which is basically a giant Attached Dependency property. This connects the ‘behaviors’ to the FrameworkElements:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.ApplicationModel;

namespace WinRtBehaviors
{
  /// <summary>
  /// Attached dependency property storing 'behaviors'
  /// </summary>
  public static class Interaction
  {
    public static readonly DependencyProperty BehaviorsProperty =
       DependencyProperty.RegisterAttached("Behaviors",
       typeof(ObservableCollection<Behavior>),
       typeof(Interaction),
       new PropertyMetadata(
         DesignMode.DesignModeEnabled ? new ObservableCollection<Behavior>() : null,         
       BehaviorsChanged));


    /// <summary>
    /// Called when Property is retrieved
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static ObservableCollection<Behavior> GetBehaviors(DependencyObject obj)
    {
      var associatedObject = obj as FrameworkElement;
      var behaviors = obj.GetValue(BehaviorsProperty) as ObservableCollection<Behavior>;
      if (behaviors == null)
      {
        behaviors = new ObservableCollection<Behavior>();
        SetBehaviors(obj, behaviors);
      }

      return behaviors;
    }

    /// <summary>
    /// Called when Property is retrieved
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="value"></param>
    public static void SetBehaviors(
       DependencyObject obj,
       ObservableCollection<Behavior> value)
    {
      obj.SetValue(BehaviorsProperty, value);
    }

    /// <summary>
    /// Called when the property changes
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    private static void BehaviorsChanged(
     object sender,
     DependencyPropertyChangedEventArgs args)
    {
      var associatedObject = sender as FrameworkElement;
      if (associatedObject != null)
      {
        var oldList = args.OldValue as ObservableCollection<Behavior>;
        if (oldList != null)
        {
          foreach (var behavior in oldList)
          {
            behavior.AssociatedObject = null;
          }
        }

        var newList = args.NewValue as ObservableCollection<Behavior>;
        if (newList != null)
        {
          foreach (var behavior in newList)
          {
            behavior.AssociatedObject = sender as FrameworkElement;
          }
          newList.CollectionChanged += (collectionSender, collectionArgs) =>
          {
            switch (collectionArgs.Action)
            {
              case NotifyCollectionChangedAction.Add:
                {
                  foreach (Behavior behavior in collectionArgs.NewItems)
                  {
                    behavior.AssociatedObject = associatedObject;
                  }
                  break;
                }
              case NotifyCollectionChangedAction.Reset:
              case NotifyCollectionChangedAction.Remove:
                {
                  foreach (Behavior behavior in collectionArgs.NewItems)
                  {
                    behavior.AssociatedObject = null;
                  }
                  break;
                }
            }
          };
        }
      }
    }
  }
}

So what do we have here? On top, a pretty standard way of registering an attached dependency property – an ObservableCollection of Behavior. Notice the fact the initial value is null in runtime, but an empty collection in design time. This is because of the next part, the mandatory GetBehaviors method. This is normally ‘just a getter’, but it checks if the collection is null first. And then something interesting happens:

  • If it is null, it creates a new empty collection and initializes the attached dependency property itself with it.
  • That, in turn, fires BehaviorsChanged
  • BehaviorsChanged attaches an internal anonymous method to the ObservableCollectionChanged event of the behavior collection.
  • That anonymous method basically rams the FrameworkElement to which this ObservableCollection is attached in the AssociatedObject property of every new behavior that’s added the list.
  • This will fire the overrideable OnAttached method in the bavhior and boom – your behavior is ready to go.

The SetBehaviors method then is pretty standard. The basic pattern of a behavior is then something like this:

namespace Win8nl.Behaviors
{
  public class DragFlickBehavior : AttachedBehavior<FrameworkElement>
  {
    protected override void OnAttached()
    {
      // Do something
      base.OnAttached();
    }
    protected override void OnDetaching()
    {
      // Do something
      base.OnDetaching();
    }
  }
}

Which, not entirely by accident, looks quite a lot like an behavior looks in Windows Phone or Silverlight. And you call it in XAML like this:

<Page
  x:Class="Catchit8.BlankPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="using:Catchit8"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:Win8nl_Behaviors="using:Win8nl.Behaviors"
  xmlns:WinRtBehaviors="using:WinRtBehaviors"
  mc:Ignorable="d">

  <Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
    <TextBlock HorizontalAlignment="Left" Margin="503,213,0,0" TextWrapping="Wrap" 
   VerticalAlignment="Top" FontSize="18" Text="Drag me">
      <WinRtBehaviors:Interaction.Behaviors>
         <Win8nl_Behaviors:DragFlickBehavior BrakeSpeed ="5"/>
      </WinRtBehaviors:Interaction.Behaviors>
    </TextBlock>
    <Button Content="Drag me too!" HorizontalAlignment="Left" Margin="315,269,0,0" 
   VerticalAlignment="Top" >
      <WinRtBehaviors:Interaction.Behaviors>
          <Win8nl_Behaviors:DragFlickBehavior BrakeSpeed ="5"/>
      </WinRtBehaviors:Interaction.Behaviors>
     </Button>

  </Grid>
</Page>

Like I said: crude, clumsy and no Blend support. There are a few issues with it. The anonymous method doing all the work, is never detached. I wonder how much memory leaks this will produce. But at least I can move forward now porting a lot of stuff I made for Windows Phone to Windows 8. Unfortunately, contrary to what I hoped, data binding to dependency properties of the behavior itself does not seem to work yet (thanks to Filip Skakun for pointing that out in a reaction to that this post) [It does now, see below]. I hope people smarter than me can improve this to a possible better solution. I will soon post a demo solution with the DragFlickBehavior in working condition in it, after I have traced back how I got it working it the first place.

In the mean time, I’ve started an CodePlex project that will be the home of this stuff. I was initially planning of including it in my Win8nl CodePlex library (coming soon) but after careful consideration and advice, I decided to make a separate library. After all, if people would like to go ahead and expand this, adding triggers and whatnot, it would probably interfere with my own ‘hobby’ library and vice versa.

So Metro, meet behaviors, at “WinRtBehaviors”.

Very much thanks to Geert van Horrik for the suggestions that led to the improvement of my first version.  He is as of now registered as a developer on WinRtBehaviors.

Update 04-04-2012 The WinRtBehaviors library is adapted: a) it no longer introduces memory leaks as described, (thanks to Geert van Horrik), and b) Filip Skakun provided me with code to actually enable data binding.

25 March 2012

Instant language update in a Windows Phone 7 application using MVVM

With more and more Marketplaces being added to the Windows Phone ecosystem, globalization of your app becomes all the more more important. I am happy to see a lot of apps these days support multiple languages. I usually employ a solution described in my article MVVMLight based language selection for Windows Phone 7 – this automatically picks up the language from the phone and defaults to English/US if the default language is not supported by the app. But I also like to give the user the chance to override the automatically selected language. I, for instance, run the phone OS in English/US, but I’d like specific Dutch apps to run in Dutch, thank you. If you follow the globalization example as described by MSDN using my code, that unfortunately requires the app to be restarted after applying the language change. Well, no more!

The sample as provided by MSDN provides a localized string helper that looks more or less like this:

namespace InstantLanguage.Resources
{
  public class LocalizedStrings
  {
    public LocalizedStrings()
    {
    }
  
    private static AppResources localizedResources = new AppResources();

    public AppResources LocalizedResources 
    { 
      get { return localizedResources; } 
    }
  }
}

I employed it since my first localized app, and I usually put it in the same directory as my resource files. You declare it in your App.xaml like this:

<Application.Resources>
    <!-- More resources -->
    <Resources:LocalizedStrings x:Key="LocalizedStrings"/>
</Application.Resources>
Of course you need to declare the namespace for LocalizedResources first, so in the top you will add something like
xmlns:Resources="clr-namespace:InstantLanguage.Resources"

And now you can use it to show localized strings by binding to it like this:

<TextBlock x:Name="ApplicationTitle" 
  Text="{Binding LocalizedResources.AppTitle, Source={StaticResource LocalizedStrings}}" 
  Style="{StaticResource PhoneTextNormalStyle}"/>

But unfortunately, as I said, if you change the language, for instance by calling this code, nothing happens.

Thread.CurrentThread.CurrentUICulture = new CultureInfo("de-DE");
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture;

The reason for this is pretty simple: the app has no way of informing the GUI, as LocalizedStrings has no support for INotifyPropertyChanged. But that can be easily fixed, by taking MVVMLight and implement LocalizedStrings as a child class of ViewModelBase:

using System.Windows;

namespace InstantLanguage.Resources
{
  using GalaSoft.MvvmLight;

  public class LocalizedStrings : ViewModelBase
  {
    private static AppResources localizedresources = new AppResources();

    public AppResources LocalizedResources
    {
      get { return localizedresources; }
    }

    public void UpdateLanguage()
    {
      localizedresources = new AppResources();
      RaisePropertyChanged(() => LocalizedResources);
    }

    public static LocalizedStrings LocalizedStringsResource
    {
      get
      {
        return Application.Current.Resources["LocalizedStrings"]
            as LocalizedStrings;
      }
    }
  }
}

and then there is another matter: the app needs to have a way of kicking off the RaisePropertyChanged event on a class used as a StaticResource. That’s what UpdateLanguage and the static LocalizedStringsResource are for. Note that for this to work, you will need to have defined the resource with the key LocalizedStrings:

<Resources:LocalizedStrings x:Key="LocalizedStrings"/>

The keys that need to match are marked in red in both pieces of code. Anyway, after you changed the language, call:

LocalizedStrings.LocalizedStringsResource.UpdateLanguage();

And boom – all your texts coming from the resource file will update instantaneously.

InstantLanuage

Here you can find a sample solution demonstrating this principle using the newest release of my #wp7nl library on codeplex and the technique I mentioned earlier. There’s a simple viewmodel subclassing LanguageSettingsViewModel, and you can see the call to UpdateLanguage directly after the language setting has been changed:

using System.ComponentModel;
using InstantLanguage.Resources;
using Wp7nl.Globalization;

namespace InstantLanguage.ViewModel
{
  /// <summary>
  /// Main view model. By subclassing LanguageSettingsViewModel we get properties 
  /// "SupportedLanguages" and "CurrentLanguage" for free. 
  /// </summary>
  public class MainViewModel : LanguageSettingsViewModel
  {

    public MainViewModel()
    {
      // Note: en-US is added by default
      AddLanguages(new Language 
	    { Description = "Deutsch", Locale = "de-DE" });
      AddLanguages(new Language 
	   { Description = "Nederlands", Locale = "nl-NL" });
      PropertyChanged += MainViewModelPropertyChanged;
    }

    void MainViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      if (e.PropertyName == "CurrentLanguage")
      {
        SetLanguageFromCurrentLocale();
        LocalizedStrings.LocalizedStringsResource.UpdateLanguage();
      }
    }
  }
}

In the end, it’s always pretty simple.

Note that for this globalization and the code to work properly, you will need to take into account the following:

  • You will need to change the “Custom Tool” property for each resource file to “PublicResXFileCodeGenerator” (default is ResXFileCodeGenerator)
  • You will need to have a .resx file for every language you support. For the default language I call mine usually AppResources.resx, for German I then have to define AppResources.de-DE.resx, etc.
  • You will also need to add supported extra languages in the SupportedCultures tag in your main project. You need to do manually, by opening the project file in for instance notepad. Why this can’t be done from Visual Studio – don’t ask me, I’m just the messenger here ;-). To support Dutch and German for instance you change the tag’s contents to:
<SupportedCultures>nl-NL;de-DE</SupportedCultures>

Note: I got the idea from a gentleman I met at the 2012 edition of the Microsoft Techdays in The Hague while while I was manning at the Ask The Expert stand. He showed me it could be done, but did only briefly showed me some of code and certainly not all of it. Once I knew it could be done, I more or less pieced this together in the moments I was not being bombarded by questions from developers dropping by. Unfortunately I don’t remember the gentleman’s name, or else I would have added some credits.

Have fun! I hope this helps you storm the new Marketplaces! ;-)

04 March 2012

MVP Summit 2012–the day after

So here I am, back in the Netherlands, after the MVP Summit 2012, still quite dazed from what hit me. It was my very first summit, and I was not quite sure what to expect. In retro respect the most amazing thing was what might be called ‘super Tuesday’. I spent a whole day with my fellow Windows Phone MVP’s and the product team. Suddenly all the people I only conversed with on twitter or live messenger were in one room: people like Ginny Caugey (I finally now know how to pronounce her last name), Matt Hidinger, Den ‘DennisCode’ Delimarsky (he actually is recognizable from his XBox avatar), Atley Hunter (who I think deserves the nickname ‘Fast Forward’) , Nick ‘ActiveNick’ Landry, Peter Novak, Rudy Huyn, to name just a few – and we where joined by people of the product team, which included Cliff Simpkins, Ben Lower and a few more whose name I omit because I am not even sure if I can mention them without spilling some beans. I feel a bit lame about this, but I like to err on the side of caution. For I must admit that when I got the award and was requested to sign an NDA documents it felt a bit over the top because frankly, I haven’t been seeing very much that required NDA for since October. Up until that remarkable Tuesday. I cannot go into any detail, other than that it was somewhat of a bewildering experience. For obvious reasons Microsoft are keeping their cards very close to the chest about anything related to Windows Phone, and this was quite different. To say the contents were interesting is like calling Mount Everest a ‘pretty steep hill’. After the formal sessions there was also an off-site event, which was at the Lucky Strike Lanes in Bellevue, where we got to know each other in a more informal way. I am still not sure who are the loudest, Canadians or Michiganians, but they give each other a run for the decibels ;-)

Another interesting observation I made during some sessions over the week was related to the recent row on the internet about MVP’s being Microsoft “marketing puppies”. Let me tell you this: behind doors, Microsoft asks for feedback and boy, do they get feedback from their MVP’s. A lot of typing and/or scribbling is being done while this is going on. Debates sometimes get pretty heated, but the setting is always a polite, frank exchange of ideas. Microsoft listens, listens intensely, and not only this occasion. But I feel a lot depends on how you put it in words. Think about what you would like to achieve before you spill your frustrations in a four-letter-words rant on the internet next time.

So what did I take from this occasion?

  1. A lot of solid information. I was told this differs from product group to product group; Windows Phone being pretty new and making aggressive movements forward, inherently is a hot spot where a lot is going on.
  2. Faces, names, and contact cards. It’s unbelievable how much people you meet. Being open to others is a natural things amongst most MVP’s – or else you would not be an MVP in the first place – and conversations start pretty easy, even for people who are bit contact shy like me. And it’s unbelievable how, in this time of online living, things are still different after you have shared a drink or talked a couple of hours into the evening. Apart from the people I mentioned above I met Rene Schulte, Laurent Bugnion, Scott Guthrie (thanks Laurent!), Scott Hanselman (sorry I pretended mixing you up with The Gu ;) ), Micheal Crump (he does exist, and I have pictures to prove it), Morten Nielsen, András Velvárt, Dave Bost, Chris Koening (both Windows Phone 7 DPE, equivalent to our own Matthijs Hoekstra), Davide Zordan (thanks for the Win8 copy!), David 'Wynapse' Campbell and I finally got the see our expertise group contact Tracey Hackney. They now have faces and voices. That still counts.
  3. Too little sleep, a crumpled back from spending too much time in a cramped aircraft chair and if I am not mistaken, a severe cold or a flue in development ;-)

Simply put: this was definitely worth the trouble.

Now that I’ve had - and survived ;-) - my first Summit, I think I have some advice for next-time-newbies:

  1. Try to get into one of the ‘central’ hotels: Hyatt, Westin, Silver Cloud or Courtyard. The other hotels are hotel-wise just a good but a lot more away from the ‘action’. Microsoft organizes a good shuttle service – but that stops at 9pm and I can assure you most times you are not done at that time. And then you have to get a cab or something.
  2. When you need to be on the campus early in the morning, allow for an hour travelling time. It’s actually more like 20 minutes, but you have to allow for traffic jams or the fact that the bus sometimes needs to take a tour along other hotels. And you do want to be on time. Some people weren’t in time for some special event organized by our Dutch MVP lead and simply missed it. Set an alarm clock, take a strong coffee (or tea) and get outta there.
  3. There’s usually a first-timers event early on the first evening. I found it useful to drop by. There were some Microsoft people and seasoned MVP’s talking with us newbies to get us in the mood. On the practical side, there’s food as well.
  4. Prepare. The Schedule Builder is sometimes confusing and I ran into a problem getting the app on my phone to run, which I only noticed being on-site. Hook up with some seasoned MVP’s up front and ask advice as to which side sessions and/or side parties to go to. I did not, and I missed a few things because of that.
  5. Download the MS Campus Maps app on your Windows Phone. It’s indispensible for getting around and getting directions.
  6. Download My ContacTile  on your Windows Phone – it creates a QR code of your contact info to quickly share via Bing Vision.
  7. Hotel rooms are for storing suitcases, taking a quick shower and some essential sleeping. That’s all. Don’t spend time there. Although most expenses during the Summit are covered I think I paid about €800 to fly to the USA and stuff so I made sure I got the most out of it. I figured I would pay the fatigue price later (like now :-) ).
  8. Bring power. Have a charger with you all the time and/or buy yourself a simple ‘USB juice pack’. It’s also a great way to help out fellow MVP’s who did not bring one, so they can borrow yours. A great conversation starter if any.
  9. When you are in a shuttle bus and get to sit next to a stranger, or at the breakfast room: introduce yourself and talk. Ask after their expertise and what they are working on. I was able to help out two people by showing them a blog post I wrote.
  10. The attendee party is big and spectacular and usually the last occasion to meet everyone. Do go there and have fun.
  11. Especially on the first days you want to be sharp. Keep down the booze and try to get a least some sleep.
  12. Keep your NDA. Watch your use of social media. Don’t discuss things outside your product group or in public places. The hardest part is not to correct incorrect speculations or react with body language ;-). I resorted to “I’d rather talk about something else now” at one point. It made me respect the product group members who have to make this mental juggle all the time very much.

Some special advice to MVP’s from outside the USA:

  1. Make sure you have enough cash on you to be able to tip people. That’s what you do in the USA. Familiarize yourself with the unwritten rules. 10% is ok, 15% if you are really satisfied. Cafeteria and such sometimes don’t expect a tip to be handed over directly but then there is usually a kind of can where you can drop the change. It’s not always obvious to me.
  2. Even more important: make sure you have a credit card. You don’t exist without a credit card in the USA.
  3. There is free Wi-Fi almost everywhere but for whatever deity you care to believe in’s sake, get yourself a $25 AT&T 500mb data plan so you are online all the time so you can find your fellow MVP’s or use online maps and stuff like that. Also, it’s great for keeping contact with the home front. AT&T first tests your phone if it works at all and even configures it before they charge you.
  4. Buses and light trail in Seattle are dirt cheap. Inside the city you pay about $2.25 for a trip from any given place to another and if you get back within a few hours, the return trip is free. The 550 bus from downtown to Bellevue costs $2.50. It partly runs inside the light rail tunnel, that’s why I couldn’t find it at first. There is also the 560 bus from SEA-TAC to Bellevue. Same price.
  5. Visit the Microsoft Store in the Bellevue mall. You won’t believe your eyes. Think hard before you take your credit card along ;-)
  6. Bring an umbrella. Seattle weather is erratic at best of times, but in February you can get everything from sunny weather to snow an back again in one day. People from the Netherlands or the UK just have to pretend they prepare for the weather at home ;-)

22 February 2012

Behavior to show a MessageBox from a ViewModel

Since I am a very lazy programmer and I not always want to register or define services and whatnot for showing a simple message I created this extremely simple behavior for showing a MessageBox from a ViewModel. I wrote the code for Windows Phone 7, but I suppose it could be used in Silverlight as well.

The behavior itself is as simple as this:

using System.Windows;
using System.Windows.Interactivity;

namespace Wp7nl.Behaviors
{
  public class MessageBoxDisplayBehavior : Behavior<FrameworkElement>
  {
    public const string MessagePropertyName = "Message";

    public string Message
    {
      get { return (string)GetValue(MessageProperty); }
      set { SetValue(MessageProperty, value); }
    }

    public static readonly DependencyProperty MessageProperty =
      DependencyProperty.Register(
        MessagePropertyName,
        typeof(string),
        typeof(MessageBoxDisplayBehavior),
        new PropertyMetadata(string.Empty, MessageChanged));

    public static void MessageChanged(DependencyObject d, 
      DependencyPropertyChangedEventArgs e)
    {
      var msg = e.NewValue as string;
      if (!string.IsNullOrEmpty(msg))
      {
        MessageBox.Show(msg);
      }
    }
  }
}

Easiest way to use it is to just drag it onto about any control in your Page using Blend, as long as it has the right data context, and select a string property to bind it to. In XAML, it usage looks something like this:

<Grid DataContext="{Binding ChooserViewModel}">
 <i:Interaction.Behaviors>
  <Wp7nl_Behaviors:MessageBoxDisplayBehavior 
    Message="{Binding MessageBoxMessage, Mode=TwoWay}"/>
 </i:Interaction.Behaviors>

Just one the one thing you might want to consider is how you define “MessageBoxMessage” in your ViewModel. I defined it as follows:

private string messageBoxMessage;
public string MessageBoxMessage
{
  get { return messageBoxMessage; }
  set
  {
    messageBoxMessage = value;
    RaisePropertyChanged(() => MessageBoxMessage);
  }
}
as opposed to what you would normally do, which is
private string messageBoxMessage;
public string MessageBoxMessage
{
  get { return messageBoxMessage; }
  set
  {
    if (messageBoxMessage != value)
    {
      messageBoxMessage = value;
      RaisePropertyChanged(() => MessageBoxMessage);
    }
  }
}
I intentionally left out the value check (in red), so RaisePropertyChanged always fires. This gives the real stupid mentally challenged users that make the same error multiple times multiple message boxes as well ;-)

06 February 2012

JSON deserialization with JSON.net: class hierarchies

In part 1 of this series I described the basics of creating classes from a JSON string and then simply deserializing the string into a (list of) classes. That way, you don’t have all the hooplah of SOAP, but still have strongly-typed classes in your client app. But beware, there is no formal contract either, so on a beautiful morning you might start to think that either you had too much of a drink yesterday evening, or that the company providing the data feed for your app indeed has started to sell Windows Phone 7 devices made by Sony, with a 65” screen.

Looking at the JSON string you now see something like this:

[
  {
    "Brand": "Nokia","Type" : "Lumia 800", "Device" : "Phone",
    "Specs":{"Storage" : "16GB", "Memory": "512MB","Screensize" : "3.7"}
  },
  {
    "Brand": "Sony", "Type" : "KDL-65HX920","Device" : "TV",
    "Specs":{"Screensize" : "65", "FullHD" : "Yes", "ThreeD" : "Yes" }
  },  
  { "Brand": "Nokia","Type" : "Lumia 900","Device" : "Phone",
    "Specs":{"Storage" : "8GB", "Memory": "512MB","Screensize" : "4.3" }
  },
  {
    "Brand": "Samsung", "Type" : "UE55C9000","Device" : "TV",
    "Specs":{"Screensize" : "55", "FullHD" : "Yes", "ThreeD" : "Yes" }
  },  
]
None of the two options mentioned before appear to be true: apparently the company has diversified. They are now selling TV's as well. Of course you could run this trough json2csharp, which will give you this:
public class Specs
{
    public string Storage { get; set; }
    public string Memory { get; set; }
    public string Screensize { get; set; }
    public string FullHD { get; set; }
    public string ThreeD { get; set; }
}

public class RootObject
{
    public string Brand { get; set; }
    public string Type { get; set; }
    public string Device { get; set; }
    public Specs Specs { get; set; }
}

This will work, but not for the purpose of what I’d like to show. We refactor the whole stuff into an object structure like this:

CodeScheme

Or, in code (put into a single file for the sake of brevity)

namespace JsonDemo
{
  public abstract class Device
  {
    public string Brand { get; set; }
    public string Type { get; set; }
  }
  
  public class Phone : Device
  {
    public PhoneSpecs Specs { get; set; }
  }
  
  public class Tv : Device
  {
    public TvSpecs Specs { get; set; }
  }
  
  public abstract class Specs
  {
    public string Screensize { get; set; }
  }
  
  public class PhoneSpecs : Specs
  {
    public string Storage { get; set; }
    public string Memory { get; set; }
  }
  
    public class TvSpecs: Specs
  {
    public string FullHd { get; set; }
    public string ThreeD { get; set; }
  }
}

If you think this is a ludicrous complicated way to store such a simple data structure I think you are quite right, but a) they don’t call me a Senior Software Architect for nothing, making things complicated is what I am told Architects do for a living, so I try to be a Good Boy ;-) and b) this is just for the purpose of the sample, so bear with me, right?

If you remember the crux of part 1: it all came down to one line of code, namely:

JsonConvert.DeserializeObject<List<Phone>>(r.EventArgs.Result);

This method actually has a second parameter: params Newtonsoft.Json.JsonConverter[] converters, which allows you to provide your own custom converters. Making those is pretty easy, and it becomes even more easy when you use the JsonCreationConverter<T> class that’s floating around the internet in various permutations. I nicked it from StackOverflow here.

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace JsonDemo
{
  public abstract class JsonCreationConverter<T> : JsonConverter
  {
    protected abstract T Create(Type objectType, JObject jsonObject);

    public override bool CanConvert(Type objectType)
    {
      return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, 
      object existingValue, JsonSerializer serializer)
    {
      var jsonObject = JObject.Load(reader);
      var target = Create(objectType, jsonObject);
      serializer.Populate(jsonObject.CreateReader(), target);
      return target;
    }

    public override void WriteJson(JsonWriter writer, object value, 
   JsonSerializer serializer)
    {
      throw new NotImplementedException();
    }
  }
}

To make a converter, you subclass this object into a templated converter for the base class, override the “Create” method and off you go. Writing a converter for the Device/Phone/Tv hierarchy is pretty easy: you just have to check the value of the “Device” property, which you do like this:

using System;
using Newtonsoft.Json.Linq;

namespace JsonDemo
{
  public class JsonDeviceConverter : JsonCreationConverter<Device>
  {
    protected override Device Create(Type objectType, JObject jsonObject)
    {
      var typeName = jsonObject["Device"].ToString();
      switch(typeName)
      {
        case "TV":
          return new Tv();
        case "Phone":
          return new Phone();
        default: return null;
      }
    }
  }
}

For the Specs/PhoneSpecs/TvSpecs you do more or less the same, only now you have to check for the existence of certain properties, not the value. I decided that if an object has a “Storage” property it’s a PhoneSpecs, and if it has “FullHD” it’s a TVSpecs.

using System;
using Newtonsoft.Json.Linq;

namespace JsonDemo
{
  public class JsonSpecsConverter : JsonCreationConverter<Specs>
  {
    protected override Specs Create(Type objectType, JObject jsonObject)
    {
      if(jsonObject["Storage"] != null)
      {
        return new PhoneSpecs();
      }

      if (jsonObject["FullHD"] != null)
      {
        return new TvSpecs();
      }

      return null;
    }
  }
}

Finally, to get these converters being used by the deserializer you have to slightly modify the deserialization line:

var deserialized = JsonConvert.DeserializeObject<List<Device>>(r.EventArgs.Result, 
  new JsonDeviceConverter(), new JsonSpecsConverter());

And sure enough, if you put a breakpoint behind this line, you can place a watch on “deserialized” and see that the data structure has been deserialized in our artfully crafted class structure.

Deserialized

And that’s all there is to it. Demo solution with full code can be found here.

The third and final part of this series shows how to cache results and can be found here