Safe event detachment ‘pattern’ for behaviors
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 ;-)