Archive

Posts Tagged ‘Josh Smith’

It’s not that hard to add the missing bits to Silverlight.

July 19, 2009 12 comments

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. it 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
{
  /// <summary>
  /// Provides an implementation so that you can use the
  /// "weak event listener" pattern to attach listeners
  /// for the <see cref="PropertyChanged" /> event.
  /// </summary>
  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
    /// <summary>
    /// Adds the specified listener to the list of listeners on the specified source.
    /// </summary>
    ///
<param name="source">The object with the event.</param>
    ///
<param name="listener">The object to add as a listener.</param>
    ///
<param name="propertyName">The name of the property that exists on
    /// source upon which to listen for changes.</param>
    public static void AddListener(INotifyPropertyChanged source,
      IWeakEventListener listener,
      string propertyName)
    {
      Instance.PrivateAddListener(source, listener, propertyName);
    }

    /// <summary>
    /// Removes the specified listener from the list of listeners on the
    /// specified source.
    /// </summary>
    ///
<param name="source">The object with the event.</param>
    ///
<param name="listener">The object to remove as a listener.</param>
    ///
<param name="propertyName">The name of the property that exists
    /// on source upon which to listen for changes.</param>
    public static void RemoveListener(INotifyPropertyChanged source,
      IWeakEventListener listener,
      string propertyName)
    {
      Instance.PrivateRemoveListener(source, listener, propertyName);
    }
    #endregion

    /// <summary>
    /// Get the current instance of <see cref="PropertyChangedEventManager"/>
    /// </summary>
    private static PropertyChangedEventManager Instance
    {
      get
      {
        if (_manager == null)
          _manager = new PropertyChangedEventManager();
        return _manager;
      }
    }

    /// <summary>
    /// Begin listening for the <see cref="PropertyChanged"/> event on
    /// the provided source.
    /// </summary>
    ///
<param name="source">The object on which to start listening
    /// for <see cref="PropertyChanged"/>.</param>
    private void StartListening(INotifyPropertyChanged source)
    {
      source.PropertyChanged += new PropertyChangedEventHandler(this.PropertyChanged);
    }

    /// <summary>
    /// Stop listening for the <see cref="PropertyChanged"/> event on the
    /// provided source.
    /// </summary>
    ///
<param name="source">The object on which to start listening for
    /// <see cref="PropertyChanged"/>.</param>
    private void StopListening(INotifyPropertyChanged source)
    {
      source.PropertyChanged -= new PropertyChangedEventHandler(this.PropertyChanged);
    }

    /// <summary>
    /// The method that handles the <see cref="INotifyPropertyChanged.PropertyChanged"/> event.
    /// </summary>
    ///
<param name="sender">The source of the event.</param>
    ///
<param name="args">A <see cref="PropertyChangedEventArgs"/> that
    /// contains the event data.</param>
    private void PropertyChanged(object sender, PropertyChangedEventArgs args)
    {
      List<WeakReference> list = _list[args.PropertyName];
      if (list != null)
      {
        // We have the listeners. Deal with them
        foreach (WeakReference item in list)
        {
          IWeakEventListener eventItem = item.Target as IWeakEventListener;
          if (eventItem != null && item.IsAlive)
          {
            eventItem.ReceiveWeakEvent(this.GetType(), sender, args);
          }
        }
      }
    }

    /// <summary>
    /// Private method to add the specified listener to the list of listeners
    /// on the specified source.
    /// </summary>
    ///
<param name="source">The object with the event.</param>
    ///
<param name="listener">The object to add as a listener.</param>
    ///
<param name="propertyName">The name of the property that exists
    /// on source upon which to listen for changes.</param>
    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);
        }
        // Now, start listening to source
        StartListening(source);
      }
    }

    /// <summary>
    /// Private method to remove the specified listener from the list of listeners
    /// on the specified source.
    /// </summary>
    ///
<param name="source">The object with the event.</param>
    ///
<param name="listener">The object to remove as a listener.</param>
    ///
<param name="propertyName">The name of the property that exists on
    /// source upon which to listen for changes.</param>
    private void PrivateRemoveListener(INotifyPropertyChanged source,
      IWeakEventListener listener,
      string propertyName)
    {
      if (_list != null)
      {
        lock (SyncLock)
        {
          if (_list.ContainsKey(propertyName))
          {
            // Stop responding to changes
            StopListening(source);
            // Remove the item from the list.
            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
{
  /// <summary>
  /// Provides event listening support for classes that expect to receive events
  /// through the WeakEvent pattern and a WeakEventManager.
  /// </summary>
  public interface IWeakEventListener
  {
    /// <summary>
    /// Receives events from the centralized event manager.
    /// </summary>
    ///
<param name="managerType">The type of the WeakEventManager calling this method.</param>
    ///
<param name="sender">Object that originated the event.</param>
    ///
<param name="e">Event data.</param>
    /// <returns>true if the listener handled the event. It is considered an error by the
    /// WeakEventManager handling in WPF to register a listener for an event that the
    /// listener does not handle. Regardless, the method should return false if it receives
    /// an event that it does not recognize or handle.
    /// </returns>
    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.

Action based ViewModel and Model validation.

January 22, 2009 20 comments

A while ago, WPF guru Josh Smith blogged about using the ViewModel to provide a first line of defence when validating data using the MVVM model. In his article, Josh talks about having the ViewModel perform some validation before passing this back to the underlying model. In his example, he uses a string property to perform initial validation on a UI element before it updates an integer, so that the user can type in abc123, but the model only gets updated if it’s a valid number. Please read his article here for more information.

While I liked Josh’s implementation, there was something nagging at me – his implementation relied heavily on switch statements in the property validation, and this really goes against the grain for me. Thinking about this for a while, it seemed to me that we could achieve a similar implementation and, at the same time, provide a nice level of abstraction. To do this, we can use the Action<T> delegate to nicely decouple the validation from the actual property validation. The following class provides the underlying mechanism for separating the validation.

ModelBase class

Now, both the ViewModel and Model classes can derive from this class and get access to the same functionality. The following class demonstrates this:

Person class

The interesting bit of this class is how simple the implementation of IDataErrorInfo here actually is. All the “magic” has been taken care of by creating an Action that will invoke the ValidateAge method. By calling AddValidation on the base class and passing the name of the property that the validation applies to, we can automatically hook up to perform the validation.

You can download the sample project here. Be sure to change the extension back from Doc to Zip and then decompress it.

Follow

Get every new post delivered to your Inbox.

Join 34 other followers