A weak event listener for WPF, Silverlight and Windows Phone 7

by Geert 23. November 2011 20:28

A small note upfront: this blog post does not contain catchy images or videos, but it is truly worth reading! Try to hang in there, or if you don’t care about any memory leaks in your app, this is the time to leave.

You have probably heard about weak events before. This blog post is not about the issue of the cause of weak events, there are lots of articles about that. This article is about the solution to it. Shortly said, when you do this in every class (just for the sake of explaining the problem, don’t start thinking this code has no business value):

   1: var log = Log.Instance;
   2: log.LogReceived += OnLogReceived;

As you can see, the log is a singleton, so there is only one living instance of the Log class. It will probably live as long as the app itself. Now you might be thinking: what’s wrong with this code? Nothing, until the app starts growing and growing and your users start complaining about memory issues.

What happens here is that you subscribe to the LogReceived event of the Log class. This subscription contains 2 things:

  1. What class do I need to call (null for static, otherwise the instance of the class)
  2. What method do I need to call

So, in fact now the Log class knows about the instance of the class that just subscribed to it and holds a reference to it (how else can it deliver the event, if it doesn’t know the address). Thus, the classes that subscribe to the Log and that do no unsubscribe will never be collected by the garbage collection.

I truly hope you understand the issue. If not, I recommend to read this excellent article, it dives into the issue a bit better.

Previous attempts

Lots of people tried to solve the puzzle before. So I thought it would be pretty easy to find a class that could help me use weak events for WPF, Silverlight and Windows Phone 7. However, none of them actually worked or supported all the scenarios I had in mind. Below are a few articles I wrote before I started writing my own weak event handler:

The closest one is the first, which uses the Silverlight toolkit implementation and improved it. However, it requires the developer to use static event handlers. Say that again? Only static event handlers? 99 % of my handlers are not static! So, I started tweaking the solution until I found out I completely rewrote it.

The golden grail

So, I think I found the golden grail, just like the others before me. You might be thinking: oh no, not again. But just wait and see, and maybe I can convince you just in time to improve the memory management of your app!

We just found the golden grail!

You can find the full code attached at the bottom of this blog post (including unit tests!), but here are the most important parts.

Open instance delegates

The key feature behind this implementation of the weak event pattern is open instance delegates. You are probably wondering: what the hell are open instance delegates? Well, good question, and I will try to explain it. An open instance delegate is just as a regular delegate, it points to the method of a specific class, but the biggest difference is that it does not bind to a specific instance. This means that it can be described as: I know you live on that street (method), but I have not clue in which city (instance) that is. The instance can be specified later. The delegate for a regular event handler looks like this:

   1: public delegate void OpenInstanceHandler(TTarget @this, object sender, TEventArgs e);

The @this is nothing special, it allows us to use the this keyword so everyone knows that the target should be passed there. As you can see, it contains 3 parameters. The first one is the target (the city), the second and third parameters are the parameters of the regular event handler.

Weak references

The weak event listener creates an open instance delegate and stores both the source and target in a WeakReference class. As soon as one of these references are no longer valid, the class is unbound. The good side of this approach is that this weak event listener does not leak when the event never fires.

So, what can it handle?

The following use cases are supported:

  • Instance source (event) and instance target (handler)
  • Static source (event) and instance target (handler)
  • Instance source (event) and static target (handler)

So, actually it handles everything that can cause a memory leak via event subscriptions!

Quality assured!

Well, as far as I can guarantee the quality. I wrote the code with care and wrote several unit tests to make sure that all situations I could think of succeed:

image

So, what can’t it handle and what are the downsides?

This weak event listener follows the rules of the .NET framework. So, it cannot subscribe to private events. If you want private events, do your own hacking (the source is available, you only have to change the DefaultEventBindingFlags at the top of the class).

There are a few downsides about using a weak event listeners in general:

  • It’s notation is ugly, the “original” .NET way looks way better
  • You have to name the event by string, that sucks (if you know a better way, contact me!)
  • It can only handle events with a handler of EventHandler<TEventArgs>
  • You become a lazy developer not caring about subscriptions

Enough! Show me how to use it!

I like to distinguish event subscriptions into 4 categories, all described below.

Instance to instance
This is the situation where an instance target subscribes to an instance event. The events are unbound as soon as either the target or source are collected.

   1: var source = new EventSource();
   2: var listener = new EventListener();
   3:  
   4: var weakEventListener = WeakEventListener<EventListener, EventSource, EventArgs>.SubscribeToWeakEvent(listener, source, "PublicEvent", listener.OnPublicEvent);

Instance to static
This is the situation where a static target subscribes to an instance event. The events are unbound as soon as the source is collected.

   1: var source = new EventSource();
   2:  
   3: var weakEventListener = WeakEventListener<EventListener, EventSource, EventArgs>.SubscribeToWeakEvent(null, source, "PublicEvent", EventListener.OnEventStaticHandler);

Static to instance
This is the situation where an instance target subscribes to a static event. The events are unbound as soon as the target is collected.

   1: var listener = new EventListener();
   2:  
   3: var weakEventListener = WeakEventListener<EventListener, EventSource, EventArgs>.SubscribeToWeakEvent(listener, null, "StaticEvent", listener.OnPublicEvent);

Static to static
This is not supported because you shouldn’t be using a weak event listener here. Static events with static event handlers simply cannot cause memory leaks because both the source and the target have no instance. However, it might be possible that you subscribe to an event too many times and the event fires too many times. But again, no memory issues here.

Oh… one more thing

Please, promise me that when you use this class, you won’t scatter it all over the place. In fact, I truly believe the best way to prevent memory leaks is to know what you are doing and unsubscribe when you are not longer interested in an object.

Only when you have no idea about the lifetime of a specific object, I recommend to use weak events.

WeakEventListener.zip (14.49 kb) [Downloads: 429]

Tags:

C# | Catel | Silverlight | Windows Phone 7 | WPF

Behaviors in the spotlight: Focus

by Geert 18. November 2011 11:16

Catel contains lots of behaviors that are very useful. For more information about behaviors, see the documentation. However, this series of blog posts with “Behaviors in the spotlight” will put one behavior in the spotlights. All behaviors in Catel are compatible with WPF, Silverlight and Windows Phone 7.

Below is a screenshot of the example application that can be found at http://catelexamples.codeplex.com that contains an example of all behaviors.

image

This time, the Focus behavior is the one that is in the spotlights.

Setting the focus when the control loads

The easiest and default method is to set the focus when the associated control is loaded. In WPF, this is immediately when the control is focused. In Silverlight, there is a delay of 500 milliseconds by default, otherwise the focus is not set correctly.

   1: <ListBox ItemsSource="{Binding PersonCollection}" SelectedItem="{Binding SelectedPerson}">
   2:     <i:Interaction.Behaviors>
   3:         <catel:Focus />
   4:     </i:Interaction.Behaviors>
   5: </ListBox>

 

Setting the focus when a specific property has changed

It is possible to set the focus when a specific event occurs. For example, when the layout root gets a MouseEnter event, the focus must be set on a specific control. This can be done via the following code:

   1: <ListBox ItemsSource="{Binding PersonCollection}" SelectedItem="{Binding SelectedPerson}">
   2:     <i:Interaction.Behaviors>
   3:         <catel:Focus FocusMoment="Event" Source="{Binding ElementName=layoutRoot}" EventName="MouseEnter" />
   4:     </i:Interaction.Behaviors>
   5: </ListBox>

 

Setting the focus when a specific event has occurred

It is possible to set the focus when a specific property changes. For example, when a value is set, the focus must move on to a new control. This can be done via the following code:

   1: <ListBox ItemsSource="{Binding PersonCollection}" SelectedItem="{Binding SelectedPerson}">
   2:     <i:Interaction.Behaviors>
   3:         <catel:Focus FocusMoment="PropertyChanged" Source="{Binding }" PropertyName="MyProperty" />
   4:     </i:Interaction.Behaviors>
   5: </ListBox>

 

Demonstration video

This video demonstrates the different modes of the behavior.


Download the demo

You can download the demo here:

Behaviors demo.zip (598.50 kb) [Downloads: 207]

Tags: , ,

C# | Catel | MVVM | Silverlight | Windows Phone 7 | WPF

Released Catel 2.3

by Geert 27. October 2011 22:38

The day has come, another month has passed, so it’s time for a new version of Catel (yes, we release a new version every month)!

Official website: http://catel.codeplex.com

Documentation: http://catel.catenalogic.com

Below are the release notes:

===========
Version 2.3
===========

Release date:
=============
2011/10/27

Added/fixed:
============
(+) Added new (non-generic) overloads in ServiceLocator for registering types
(+) WP7 version now supports tombstoning out of the box (automatic view model recovery
    after recovering from tombstoning)
(+) Added support for the Castle Windsor IoC container
(+) Added option to provide a callback as default value for properties defined on the
    DataObjectBase (and thus ViewModelBase) so it is possible to truely set a default
    value for a reference type
(+) Added support to (de)serialize the DataObjectBase to json and back
(+) Added AdvancedPropertyChangedEventArgs which provides the old and new value in case
    of a property change
(+) Added full support for data annotations. For example, if a view model property is decorated
    with the RequiredAttribute, the error is gathered and even provided to the ValidateFields and
    finally exposed via the IDataErrorInfo interface (all validation eventually ends up in one place,
    the IDataErrorInfo)
(*) Only public constructors on view models are now used by the LogicBase
    (before, also private constructors were used, but this doesn't work on
    SL and WP7)
(*) Moved implementation of IXmlSerializable from SavableDataObjectBase to
    DataObjectBase
(*) .NET implementation now uses DataContractSerialization for serializing data to
    xml so the classes can be used in combination with WCF as well
(*) DataWindowButton now only accepts delegates without parameters
(*) Major performance improvements when (de)serializing interfaces via xml
(*) Registered change event handlers using RegisterProperty are no longer called on refresh
    updates (for example, errors changed). This can solve some rare bugs and is a small
    improvement on performance
(x) Fixed a bug where commands defined as ICommand are now also recognized,
    where previously only interfaces or objects implementing ICommand were
    registered
(x) Finally fixed complex xml serialization in Silverlight and WP7
(x) Fixed issue that it was possible to cancel a view model after it was already
    closed. IF a view model is closed, the Save, Cancel and Close no longer handle
    anything
(x) Fixed very rare issue in MefHelper
(x) Fixed issue for xml serialization using DataContractSerializer when collections contain
    interfaces instead of actual types
(x) Fixed issue for xml serialization with more than 3 levels deep using the default
    DataContractSerializer class
(x) Fixed issue with StackGrid being initialized several times in Silverlight
(x) Fixed issue where the WarningAndErrorValidator would clear the Source property when being
    unloaded and causing the InfoBarMessageControl not to show up again
(x) Fixed issue where the UserControlLogic would not update a value when it is initially null
    in a master/detail scenario
(x) Fixed EventToCommand issues with Windows Phone 7 Mango
(x) Fixed issue where the UserControl<TViewModel> prevented a datacontext change to be successful
    in case when the initial datacontext value was null
(-) Removed LLBLGen support (was deprecated anyway)

Tags:

C# | Catel | Windows Phone 7 | WPF | Silverlight

XamlParseException in Silverlight 4

by Geert 24. October 2011 21:18

This week I got a support call for Catel which was about the following piece of xaml that could not be used in Silverlight 4:

   1: <i:Interaction.Behaviors>
   2:     <catel:WindowBehavior ViewModelType="viewmodels:DemoWindowViewModel" 
   3:                           Save="okButton.Click" Cancel="cancelButton.Click"  />
   4: </i:Interaction.Behaviors>

It is used in the examples of Catel to show that it is possible to attach the window MVVM behavior to any type of window. To let the behavior know which view model to attach to a specific view, the type has to be defined.

The example worked perfectly on my machine, but was not working on that of the client, even though it is perfectly valid Silverlight 4 code. The user was getting the following exception:

   1: {System.Windows.Markup.XamlParseException: Failed to create a 'System.Type' from the text 'ViewModels:DemoWindowViewModel'. [Line: 13 Position: 67]
   2:   at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
   3:   at Catel.Examples.AdvancedDemo.Views.LogicInBehavior.DemoWindow.InitializeComponent()
   4:   at Catel.Examples.AdvancedDemo.Views.LogicInBehavior.DemoWindow..ctor()}

First thing I thought was: let’s check out the inner exception, but it appeared to be null. The only hint the user could give was that he was using a 64-bit development machine, while I use a 32-bit VM for the development of Catel.

So, I created a new 64-bit VM with only Visual Studio 2010 and SL4, and what seems: I can reproduce the issue! So, shall it be a 64-bit issue? Probably not, because after installing the SL5 (beta) tools, the issue went away!

So, if you encounter a XamlParseException without any references missing, try to install the SL5 (beta) tools.

Tags:

C# | Silverlight

Catel 2.2: advanced validation in view models

by Geert 27. September 2011 18:34

In Catel 2.2, the validation framework has been refactored. This means that instead of this code:

   1: /// <summary>
   2: /// Validates the field values of this object. Override this method to enable
   3: /// validation of field values.
   4: /// </summary>
   5: protected override void ValidateFields()
   6: {
   7:     if (!string.IsNullOrEmpty(FirstName))
   8:     {
   9:         SetFieldError(FirstNameProperty, "First name cannot be empty");
  10:     }
  11: }

You can now write this:

   1: /// <summary>
   2: /// Validates the field values of this object. Override this method to enable
   3: /// validation of field values.
   4: /// </summary>
   5: /// <param name="validationResults">The validation results, add additional results to this list.</param>
   6: protected override void ValidateFields(List<FieldValidationResult> validationResults)
   7: {
   8:     if (!string.IsNullOrEmpty(FirstName))
   9:     {
  10:         validationResults.Add(FieldValidationResult.CreateError(FirstNameProperty, "First name cannot be empty"));
  11:     }
  12: }

 

The DataObjectBase

If you are not known with Catel, here is a little side note. There is a DataObjectBase class, which is the base class for data objects. This class implements INotifyPropertyChanged, IDataErrorInfo, and much more interfaces. The refactoring above is applied to the DataObjectBase.

Models and view models

As you probably know, it is also very easy to map properties of a model to a view model and back using the ViewModelToModel attributes. This way, you don’t have to rewrite validation that occurs in a model in the view model again. In WPF, there is an even better way using the Expose attribute:

   1: /// <summary>
   2: /// Gets or sets the person.
   3: /// </summary>
   4: [Model]
   5: [Expose("FirstName")]
   6: [Expose("MiddleName")]
   7: [Expose("LastName")]
   8: private  Person Person
   9: {
  10:     get { return GetValue<Person>(PersonProperty); }
  11:     set { SetValue(PersonProperty, value); }
  12: }
  13:  
  14: /// <summary>
  15: /// Register the Person property so it is known in the class.
  16: /// </summary>
  17: public static readonly PropertyData PersonProperty = RegisterProperty("Person", typeof(Person));

This way, the model Person is protected from the view, and only the FirstName, MiddleName and LastName are exposed to the view. Using either the ViewModelToModel or Expose attributes also takes care of validation as long as the model implements IDataErrorInfo or INotifyDataErrorInfo.

Advanced validation in view models

Now the cool thing is the combination of these two. As said before, the view model automatically validates the models and sets the error on the mapped view model properties. However, thanks to the new ValidateFields and ValidateBusinessRules methods that provide a list of errors, you can even customize the errors.

Now you might be thinking: so what? Well, think of a situation where the DAL (your entities) implement validation. You really don’t want to implement lots of different languages for the validation. In that case, you can use this method:

Setting constant validations in the DAL or validation layer

In the DAL, when an entity is validated, use something like this (pseudo-code):

   1: SetFieldError(“FirstName”, “FirstNameRequired”);

Translating validation in the view model

Then, in the view model, you can easily modify these errors:

 

   1: /// <summary>
   2: /// Validates the field values of this object. Override this method to enable
   3: /// validation of field values.
   4: /// </summary>
   5: /// <param name="validationResults">The validation results, add additional results to this list.</param>
   6: protected override void ValidateFields(List<FieldValidationResult> validationResults)
   7: {
   8:     foreach (var validationResult in validationResults)
   9:     {
  10:         if (validationResult.Message == "FirstNameRequired")
  11:         {
  12:             validationResult.Message = Properties.Resources.FirstNameRequired;
  13:         }
  14:     }
  15: }

Of course this is not something you want to actually do in your view model, so you’ll probably have to write a helper class that translates the validation for you.

 

Conclusion

You might or might not like delaying the translation of the model errors to as close as the view, but it shows how extremely powerful the improved validation of Catel is. And if you think a bit about it, wouldn’t it be a good idea to delay the translation from the server to the actual client to as close as the view…?

Tags: ,

C# | Catel | MVVM | Silverlight | Windows Phone 7 | WPF


About the Author

Geert van Horrik is an independent freelance software developer since January 1st, 2007. Since then he was been working on several projects from C++ to C# (WPF, Silverlight, ASP.NET, etc). Currently he loves to write his software using WPF (or Silverlight if WPF isn't an option).

Lately, Geert is spending a lot of time on Catel, a free open-source MVVM Framework for WPF and Silverlight. Actually, it's more than "just" an MVVM Framework, it's a complete application library!