Safe event detachment ‘pattern’ for behaviors

2 minute read

For the past few weeks I’ve been hacking away at my Windows Phone 7 game and noticed something odd. The objective is to intercept moving objects on the screen and drag/flick them to a target – but as the levels progressed interception and movement became ever more difficult and slow. At least that was reported by my #wp7nl fellow members who were kind (or crazy) enough to test drive the game into the higher levels. I won’t mention names here – yet ;-).

I followed the usual pattern for a behavior – at least, I think it is the usual pattern

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

namespace SomeNamespace
{
  public class DemoBehavior : Behavior<FrameworkElement>
  {
    protected override void OnAttached()
    {
      base.OnAttached();
      AssociatedObject.Loaded += AssociatedObjectLoaded;
    }

    void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
    {
      // Hook up a bunch of events to the AssociatedObject
    }

    protected override void OnDetaching()
    {
      AssociatedObject.Loaded -= AssociatedObjectLoaded;
      // Unhook the other events from the AssociatedObject
      base.OnDetaching();
    }
  }
}

I had seen this problem before while programming in WPF and indeed – when I put a breakpoint in the first line of the OnDetaching  override it was never called. Apparently Windows Phone 7 has the same problem. So here was my game, happily creating a lot of moving objects with each 1-2 behaviors attached to it listening to a bunch of events. They were never unhooked when the objects were deleted. So within a few levels the game started eating resources like there’s no tomorrow.

I modified my behaviors to try to do a general cleanup not only when OnDetaching is called but also when the AssociatedObject is unloaded. This gave me a pattern which I think might be beneficial to everyone who is working with behaviors in general:

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

namespace SomeNamespace
{
  public class BetterDemoBehavior : Behavior<FrameworkElement>
  {
    #region Setup
    protected override void OnAttached()
    {
      base.OnAttached();
      AssociatedObject.Loaded += AssociatedObjectLoaded;
      AssociatedObject.Unloaded += AssociatedObjectUnloaded;
    }

    void AssociatedObjectLoaded(object sender, RoutedEventArgs e)
    {
      // Hook up a bunch of events to the AssociatedObject
    }
    #endregion

    #region Cleanup
    private bool _isCleanedUp;

    private void Cleanup()
    {
      if (!_isCleanedUp)
      {
        _isCleanedUp = true;
        AssociatedObject.Loaded -= AssociatedObjectLoaded;
        AssociatedObject.Unloaded -= AssociatedObjectUnloaded;
        // Unhook the other events from the AssociatedObject
      }
    }

    protected override void OnDetaching()
    {
      Cleanup();
      base.OnDetaching();
    }

    void AssociatedObjectUnloaded(object sender, RoutedEventArgs e)
    {
      Cleanup();
    }
    #endregion
  }
}

And because I am not very partial to repetitive work, I created a snippet for this piece of code.

I guess that if both and Windows Phone 7 and WPF have this issue, Silverlight will have it as well. I hope at least this will help people who are, like me, stubborn enough to use Silverlight and MVVMLight to develop games for Windows Phone 7 ;-)