17 October 2013

Introducing Win8nl for Windows 8.1–kissing WinRtBehaviors goodbye

Updated November 11, 2013
It was at my very first MVP Summit in February 2012 that I learned that Windows 8 XAML was not to have any behaviors. I remember distinctly being very angry first, telling the presenter I would be “dead in the water”, then upon reflection, calming down, and deciding to find out if I could plug the hole myself. And so I did, with the help of (in order of appearance) Geert van Horrik, Filip Skakun, and AndrĂ¡s VelvĂ¡rt.

WinRtBehaviors saw the light in Spring 2012, shortly after said Summit. With 4,634 downloads, it being used in well-known top apps like Shazam, and being specifically mentioned at the 2013 edition of TechDays in both Belgium and The Netherlands (much to the surprise of colleagues and friends visiting there, not to mention myself) – I think I quite made my mark in the Windows XAML stack.

I also learned the downsides of having such a successful library, i.e. a lot of support requests. But today Visual Studio 2013 was released, and along with it the final version of the Behaviors SDK, so I released a version of Win8nl – the library with ported Windows Phone code that I built on top of WinRtBehaviors – and I made a yet-to-release version of Catch’em Birds for Windows on it, that totally works (since very recently) on that new version of Win8nl. So the days of the old WinRtBehaviors warhorse are over.

Win8nl 1.0.9 still supports Windows 8.0 and still downloads WinRtBehaviors, but if you target your app for Windows 8.1 you will see that there’s not longer a reference made to it when you download the Nuget Package.

Part of the library are the classes Behavior and Behavior<T>. These have the following implementations:
using Windows.UI.Xaml;
using Microsoft.Xaml.Interactivity;

namespace Win8nl.Behaviors
{
  /// <summary>
  /// A base class for behaviors, can be used in native environments too
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public abstract class Behavior : DependencyObject, IBehavior
  {
    public DependencyObject AssociatedObject { get; set; }

    public virtual void Attach(DependencyObject associatedObject)
    {
      AssociatedObject = associatedObject;
    }

    public virtual void Detach()
    {
    }
  }
}
This is a base class for environments where generics cannot be used - and the implementation for C# behaviors is then simple:
using Windows.UI.Xaml;

namespace Win8nl.Behaviors
{
  /// <summary>
  /// A base class for behaviors, to appear them to be identical to older frameworks
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public abstract class Behavior<T> : Behavior where T: DependencyObject
  {
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public new T AssociatedObject { get; set; }

    public override void Attach(DependencyObject associatedObject)
    {
      base.Attach(associatedObject);
      this.AssociatedObject = (T)associatedObject;
      OnAttached();
    }

    public override  void Detach()
    {
      base.Detach();
      OnDetaching();
    }


    protected virtual void OnAttached()
    {
    }

    protected virtual void OnDetaching()
    {
    }

  }
}
Based upon this class, you can port your existing behaviors from WinRtBehaviors (or from any other XAML framework, for what matters, virtually without any code change. I wrote this code in the early days of August 2013, but kind of sat on it as I wanted it to be part of the new Win8nl library. I sat a bit on it too long, as Fons Sonnemans published a very similar solution in September, so I can’t really claim prior art, but let just say it’s a logical thing to do and great minds apparently think alike. I took the EditorBrowsable thing from him, never thought of that.

What you will also notice is that the ‘hero’ behaviors EventToCommandBehavior and EventToBoundCommandBehavior, although still present, are now marked as Obsolete.This is for a very simple reason: the framework now has a standard way of doing such things. Would you use something like this in Windows 8.0 XAML:
<WinRtBehaviors:Interaction.Behaviors>
  <Win8nl_Behavior:EventToBoundCommandBehavior Event="Loaded" 
    Command="{Binding StartGame}"/>
  <Win8nl_Behavior:EventToBoundCommandBehavior Event="Unloaded" 
    Command="{Binding SuspendGame}"/>
</WinRtBehaviors:Interaction.Behaviors>
Now you can achieve the same thing an EventTriggerBehavior combined with an InvokeCommandAction.
<interactivity:Interaction.Behaviors>
  <Core:EventTriggerBehavior EventName="Loaded">
    <Core:InvokeCommandAction Command="{Binding StartGame}"/>
</Core:EventTriggerBehavior>

<Core:EventTriggerBehavior EventName="Unloaded"> <Core:InvokeCommandAction Command="{Binding SuspendGame}"/> </Core:EventTriggerBehavior> </interactivity:Interaction.Behaviors>

Update November 11, 2013
InvokeCommandAction has a CommandParameter too that you can bind to and refer to in the usual way. An interesting and useful feature of InvokeCommandAction was pointed out to me by fellow MVP Morten Nielsen – if don’t specify a CommandParameter, it defaults to passing the event arguments of the trapped event to the command. So if you have for instance this

<interactivity:Interaction.Behaviors>
  <core:EventTriggerBehavior EventName="Tapped">
    <core:InvokeCommandAction Command="{Binding TappedCommand}"/>
  </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>

Then this is still a valid command to intercept it – you just ignore the arguments

public ICommand TappedCommand
{
  get
  {
    return new RelayCommand(
        () =>
        {
          Debug.WriteLine ("Command tapped");
        });
  }
}

But this as well!

public ICommand TappedCommand
{
  get
  {
    return new RelayCommand<TappedRoutedEventArgs>(
        (p) =>
        {
          Debug.WriteLine ("Command tapped:" + p!=null);
        });
  }
}

And thus you can do something with the event arguments. So with InvokeCommandAction you can do everything you wanted to do with EventToBoundCommandBehavior.
(Update ends)
I would urge you to the utmost to no longer using EventToCommandBehavior and/or EventToBoundCommandBehavior from this day forward. Any support requests will simply be directed to this blog post ;).

Something I have been sitting on for very long – part of Catch’em Birds for Windows was a DataStateBehavior, which I basically cobbled together by reflecting into a Windows Phone 7 assembly, butchering stuff I didn’t need, understand or that did not compile, and finally arriving at something that kind of worked. For my goals. For obvious reasons I could not publish what was basically stolen and butchered Microsoft code and I am therefore very happy the Behavior SDK now has something to do the trick. If you now use DataStateBehavior in for instance a Windows Phone project like this:

<behaviors:Interaction.Behaviors>
  <behaviors_ic:DataStateBehavior Binding="{Binding IsGameOver}" 
     FalseState="GameNotOver" TrueState="GameOver" Value="true"/>
</behaviors:Interaction.Behaviors>
You can achieve the same effect by using this code:
<interactivity:Interaction.Behaviors>
  <Core:DataTriggerBehavior Binding="{Binding IsGameOver}" Value="True">
    <Core:GoToStateAction StateName="GameOver"/>
  </Core:DataTriggerBehavior>
  <Core:DataTriggerBehavior Binding="{Binding IsGameOver}" Value="False">
    <Core:GoToStateAction StateName="GameNotOver"/>
  </Core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
Thus ends the story of WinRtBehaviors on October 17, 2013. I hope you have enjoyed it, and I also hope to have given you a clear migration path.

I wish the development team lots of luck with the support requests from especially my Belgian friends, who were quite ingenious in finding scenarios in which the EventToCommand behaviors did not work properly. The best one was something like “the behavior does not work if it’s in a data template from a resource file located in another assembly”. ;)

2 comments:

Feelingweird said...

Thanks for the article!

anyone else looking for the namespaces of the interactivity or core xmlns'

they are
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"

cheers,

Joost van Schaik said...

Thanks for this addition @Feelingweird. Being and ardent Resharper user, and having been so for YEARS, I kind of tend to forget that 'the rest of the world' needs to add namespaces manually. ;)