Introduction
Recently, my good friend Josh Smith announced that he was putting together a set of MVVM foundation classes. In typical Josh fashion, these classes are hyper useful and hyper clever, and represent some of the great classes he’s produced over the last couple of years to help with MVVM.
Most of these classes work straight out of the box in both WPF and Silverlight, but there is a fly in the ointment. Josh recently developed a class to observe objects that implement INotifyPropertyChanged
and it’s seriously good; and it’s virtually totally useless in Silverlight due to Silverlight not supporting the underlying mechanism that is used in the monitoring: the PropertyChangedEventManager
class. Now, in a lesser framework than .NET, I’d be worried – but .NET gives us so much freedom to add in the missing functionality, and I wouldn’t be a WPF Disciple if I didn’t like to tinker.
Before I go any further, I will say that there is an alternative implementation in the Silverlight Toolkit in the WeakEventListener
. The problem with this class (I use the word problem advisedly here) is that it requires you to use Lambda Expressions. It also meant that the class that Josh put together would have to be modified, so I decided to see how hard it would be to put together an implementation of PropertyChangedEventManager
that works in Silverlight.
As I was working to a well defined feature set (i.e., I only had to work with the functionality in Josh’s class), I didn’t have to recreate the total functionality in the underlying framework classes. I did want it to work with the weak event pattern, however, so without further ado – here’s the functionality in Silverlight:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.Generic;
namespace System.Windows
{
public class PropertyChangedEventManager
{
#region Members
private Dictionary<string, List<WeakReference>> _list;
private static object SyncLock = new object();
private static PropertyChangedEventManager _manager = null;
#endregion
#region Public methods
public static void AddListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
Instance.PrivateAddListener(source, listener, propertyName);
}
public static void RemoveListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
Instance.PrivateRemoveListener(source, listener, propertyName);
}
#endregion
private static PropertyChangedEventManager Instance
{
get
{
if (_manager == null)
_manager = new PropertyChangedEventManager();
return _manager;
}
}
private void StartListening(INotifyPropertyChanged source)
{
source.PropertyChanged += new PropertyChangedEventHandler(this.PropertyChanged);
}
private void StopListening(INotifyPropertyChanged source)
{
source.PropertyChanged -= new PropertyChangedEventHandler(this.PropertyChanged);
}
private void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
List<WeakReference> list = _list[args.PropertyName];
if (list != null)
{
foreach (WeakReference item in list)
{
IWeakEventListener eventItem = item.Target as IWeakEventListener;
if (eventItem != null && item.IsAlive)
{
eventItem.ReceiveWeakEvent(this.GetType(), sender, args);
}
}
}
}
private void PrivateAddListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
if (_list == null)
{
_list = new Dictionary<string, List<WeakReference>>();
}
lock (SyncLock)
{
WeakReference reference = new WeakReference(listener);
if (_list.ContainsKey(propertyName))
{
_list[propertyName].Add(reference);
}
else
{
List<WeakReference> list = new List<WeakReference>();
list.Add(reference);
_list.Add(propertyName, list);
}
StartListening(source);
}
}
private void PrivateRemoveListener(INotifyPropertyChanged source,
IWeakEventListener listener,
string propertyName)
{
if (_list != null)
{
lock (SyncLock)
{
if (_list.ContainsKey(propertyName))
{
StopListening(source);
WeakReference reference = null;
foreach (WeakReference item in _list[propertyName])
{
if (item.Target.Equals(listener))
{
reference = item;
}
}
if (reference != null)
{
_list[propertyName].Remove(reference);
}
}
}
}
}
}
}
The only thing that’s missing is the weak event listener interface (IWeakEventListener
):
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace System.Windows
{
public interface IWeakEventListener
{
bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e);
}
}
That’s it – that’s all you need to add in to add the “missing” functionality. Simple, isn’t it?
You can download the source here. Note that you’ll have to rename the file from .doc to .zip before you can decompress it.