Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF

WPF: If Carlsberg did MVVM Frameworks Part 2 of n

Rate me:
Please Sign up or sign in to vote.
4.96/5 (116 votes)
7 May 2010CPOL30 min read 776.8K   157   286
It would probably be like Cinch, an MVVM framework for WPF.

Contents

Cinch Article Series Links

Introduction

Last time I simply introduced the Cinch framework, and this time, we would do a walkthrough of Cinch and its internals. This is going to be a large article, so it has actually been split over two articles. In this article, I will be covering the following:

  • Allows view to communicate LifeCycle events to a ViewModel without any hard reference links being maintained, and no IView interface requirements. There is no link at all between the View and the ViewModel.
  • Has several attached behaviours for common tasks such as:
    • Numeric text entry
    • Run an ICommand in a ViewModel based on a RoutedEvent from a XAML FrameworkElement
    • Have a group of such ICommand/RoutedEvent Events for a single XAML FrameworkElement
  • Allows the ViewModel to determine if a Model's data should be editable; the UI simply updates via bindings, based on the ViewModel driven editability state. This is available at individual Model field level, so it's very flexible.
  • Delegate validation rules which allow validation rules to be as granular as necessary.
  • Native IDataErrorInfo support using the Delegate rules approach.
  • IEditableObject usage to store/restore object state on edit / cancel edit.
  • Weak event creation, to allow the creation of WeakEvents.
  • Weak event subscription, which also allows auto unsubscriptions.
  • Mediator messaging with WeakReference support out of the box.

So I guess the only way to do this is to just start, so let's get going, shall we? But before we do that, I just need to repeat the special thanks section, with one addition, Paul Stovell who I forgot to include last time.

Prerequisites

The demo app makes use of:

  • VS2008 SP1
  • .NET 3.5 SP1
  • SQL Server (see the README.txt in the MVVM.DataAccess project to learn what you have to setup for the demo app database)

Special Thanks

Before I start, I would specifically like to say a massive thanks to the following people, without whom this article and the subsequent series of articles would never have been possible. Basically, what I have done with Cinch is studied most of these guys, seen what's hot, what's not, and come up with Cinch, which I hope addresses some new ground not covered in other frameworks.

  • Mark Smith (Julmar Technology), for his excellent MVVM Helper Library, which has helped me enormously. Mark, I know, I asked your permission to use some of your code, which you most kindly gave, but I just wanted to say a massive thanks for your cool ideas, some of which I genuinely had not thought of. I take my hat off to you mate.
  • Josh Smith / Marlon Grech (as an atomic pair) for their excellent Mediator implementation. You boys rock, always a pleasure.
  • Karl Shifflett / Jaime Rodriguez (Microsoft boys) for their excellent MVVM Lob tour, which I attended, well done lads.
  • Bill Kempf, for just being Bill and being a crazy wizard like programmer, who also has a great MVVM framework called Onyx, which I wrote an article about some time ago. Bill always has the answers to tough questions, cheers Bill.
  • Paul Stovell for his excellent delegate validation idea, which Cinch uses for validation of business objects.
  • All of the WPF Disciples, for being the best online group to belong to, IMHO.

Thanks guys/girl, you know who you are.

Cinch Internals I

This section will start the dive into the internals of Cinch. As I say, there is far too much for one article, so I am splitting the internals of Cinch over two articles. Hopefully there will be something of use to you here. Well, I hope so anyway.

View Lifecycle Events

When developing with the MVVM pattern, the holy grail is for there to be no coupling at all between the View and the ViewModel. This clean separation between the View and ViewModel is achieved in Cinch, there is no link between them at all, which is ace. However, occasionally it is advantageous for the ViewModel to know something about certain View events, such as:

  • Loaded
  • Activated
  • Deactivated
  • Close

But we just said that there was no referencing between View/ViewModel and we would like to know about these View events in the ViewModel, so how do we achieve that? The answer lies in Attached Behaviours. What we do is we have a ICommand based property in the ViewModel and we use an attached View lifecycle behaviour and attach the correct View event to the correct ViewModel ICommand. So when the View event is raised, it will actually call a ICommand implementation in the ViewModel.

Cinch provides for this using two things:

  1. A Cinch ViewModel base class called ViewModelBase, which already contains all the View lifecycle ICommand implementation you will need, which, I should point out, can be overridden in the inheritors of the Cinch ViewModelBase class
  2. Some View lifecycle attached behaviours

Let us examine one View lifecycle event, Activated, to see how it works; the others are the same.

Starting with the ViewModelBase classm we can see from the code below (extra code removed for clarity) that there is an ICommand called Activated where the trimmed Cinch ViewModelBase looks like below.

Cinch also provides public bindable (INPC) properties for each of the View lifecycle events (be careful, UserControl does not have Activated/Deactivated, these are only available on Window. These properties are set in the Cinch ViewModelBase, so overriders be aware, if you fail to call the Cinch ViewModelBase in your overrides of the lifecycle methods, these properties will not work. Anyway, here is a full example for the Activated command, the rest work the same.

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

using System.Linq.Expressions;

namespace Cinch
{

    public abstract class ViewModelBase : INotifyPropertyChanged, 
                    IDisposable, IParentablePropertyExposer
    {
        private SimpleCommand activatedCommand;
        private Boolean isActivated=false;


        /// <summary>
        /// Constructs a new ViewModelBase and wires up all the Window based Lifetime
        /// commands such as activatedCommand/deactivatedCommand/
        ///                  loadedCommand/closeCommand
        /// </summary>
        public ViewModelBase() : this(new UnityProvider())
        {

        }

        public ViewModelBase(IIOCProvider iocProvider)
        {
            activatedCommand= new SimpleCommand
            {
                CanExecuteDelegate = x => true,
                ExecuteDelegate = x => OnWindowActivated()
            };
        }

        /// <summary>
        /// Allows Window.Activated/UserControl.Activated hook
        /// Can be overriden if required in inheritors
        /// </summary>
        protected virtual void OnWindowActivated()
        {
            //Will only work as long as people, call base.OnWindowActivated()
            //when overriding
            IsActivated= true; 
        }

        /// <summary>
        /// ActivatedCommand : Window/UserControl Lifetime command
        /// </summary>
        public SimpleCommand ActivatedCommand 
        {
            get { return activatedCommand ; }
        }

        /// <summary>
        /// View Is Activated
        /// </summary>
        static PropertyChangedEventArgs isactivatedChangeArgs =
            ObservableHelper.CreateArgs<ViewModelBase>(x => x.IsActivated);

        /// <summary>
        /// Will only be reliable if users call base.OnWindowActivated()
        /// when overriding virtual ViewModelBase.OnWindowActivated()
        /// </summary>
        public Boolean IsActivated
        {
            get { return isActivated; }
            private set
            {
                isActivated = value;
                NotifyPropertyChanged(isactivatedChangeArgs );
            }
        }
    }
}

The eagle eyed amongst you may notice that there is a property called ActivatedCommand within the code above, which actually returns a SimpleCommand and not a ICommand that you may have been expecting and maybe even seen in various other posts. This is all cool, don't worry, the XAML parser and binding system are clever enough to know that any class that implements ICommand can be used in place of an ICommand in a binding. The reason to expose the ICommand as a SimpleCommand is that the SimpleCommand has an extra property that can be used from inside Unit tests. Namely, the CommandSucceeded which is set to true on Command execution completion, so can be used in Unit test Assert statements.

Here is the SimpleCommand code:

C#
using System;
using System.Windows.Input;

/// <summary>
/// This class provides a simple
/// delegate command that implements the ICommand
/// interface
/// </summary>
namespace Cinch
{
    /// <summary>
    /// Implements the ICommand and wraps up all the verbose stuff so that you 

    /// can just pass 2 delegates 1 for the CanExecute and one for the Execute
    /// </summary>

    public class SimpleCommand : ICommand
    {
        #region Public Properties
        public Boolean CommandSucceeded { get; set; }

        /// <summary>
        /// Gets or sets the Predicate to execute when the 
        /// CanExecute of the command gets called
        /// </summary>
        public Predicate<object> CanExecuteDelegate { get; set; }

        /// <summary>
        /// Gets or sets the action to be called when the 
        /// Execute method of the command gets called
        /// </summary>

        public Action<object> ExecuteDelegate { get; set; }
        #endregion

        #region ICommand Members

        /// <summary>
        /// Checks if the command Execute method can run
        /// </summary>
        /// <param name="parameter">
        ///    The command parameter to be passed</param>

        /// <returns>Returns true if the command can execute. 
        /// By default true is returned so that if the user of SimpleCommand 
        /// does not specify a CanExecuteCommand delegate the command 
        /// still executes.</returns>
        public bool CanExecute(object parameter)
        {
            if (CanExecuteDelegate != null)
                return CanExecuteDelegate(parameter);
            return true;// if there is no can execute default to true
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        /// <summary>
        /// Executes the actual command
        /// </summary>
        /// <param name="parameter">
        ///       THe command parameter to be passed</param>

        public void Execute(object parameter)
        {
            if (ExecuteDelegate != null)
            {
                ExecuteDelegate(parameter);
                CommandSucceeded = true;
            }
        }

        #endregion
    }
}

OK, so we now have a ViewModel that has a SimpleCommand for Activated that will call a protected virtual void OnWindowActivated() method, but how does this Activated SimpleCommand get executed? Well, recall I said there was some attached behaviour magic, so let's look at that next.

First, this is how the View would wire up the View Activated attached behavior:

XML
<Window x:Class="MVVM.Demo.Views.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Cinch="clr-namespace:Cinch;assembly=Cinch"
    Title="Window1" Height="300" Width="300">

      Cinch:LifetimeEvent.Activated="{Binding ActivatedCommand}"
      ....
      ....
</Window>

So this will then use the attached View lifecycle event, which is shown below.

C#
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

/// <summary>
/// This allows a Windows lifecycle events to call ICommand(s)
/// within a ViewModel. This allows the ViewModel to know something
/// about the Views lifecycle without the need for a strong link
/// to the actual View
/// </summary>

namespace Cinch
{
    /// <summary>
    /// This class is used to attach the Window lifetime events to ICommand implementations.
    /// It allows a ViewModel to hook into the lifetime of the view (when necessary) 
    /// through simple XAML tags. Supported events are Loaded, Activated, Deactivated 
    /// and Closing/Closed. For the Closing/Closed event, the CanExecute handler is invoked
    /// in response to the Closing event - if it returns true, then the Closed event is 
    /// allowed and the Execute handler is called in response.
    public static class LifetimeEvent
    {
        #region Activated
        /// <summary>
        /// Dependency property which holds the ICommand for the Activated event
        /// </summary>
        public static readonly DependencyProperty ActivatedProperty =
            DependencyProperty.RegisterAttached("Activated", 
            typeof(ICommand), typeof(LifetimeEvent),
                new UIPropertyMetadata(null, OnActivatedEventInfoChanged));

        /// <summary>

        /// Attached Property getter to retrieve the ICommand
        /// </summary>
        /// <param name="source">Dependency Object</param>
        /// <returns>ICommand</returns>

        public static ICommand GetActivated(DependencyObject source)
        {
            return (ICommand)source.GetValue(ActivatedProperty);
        }

        /// <summary>
        /// Attached Property setter to change the ICommand
        /// </summary>
        /// <param name="source">Dependency Object</param>
        /// <param name="command">ICommand</param>

        public static void SetActivated(DependencyObject source, ICommand command)
        {
            source.SetValue(ActivatedProperty, command);
        }

        /// <summary>
        /// This is the property changed handler for the Activated property.
        /// </summary>
        /// <param name="sender">Dependency Object</param>
        /// <param name="e">EventArgs</param>

        private static void OnActivatedEventInfoChanged(DependencyObject sender, 
            DependencyPropertyChangedEventArgs e)
        {
            var win = sender as Window;
            if (win != null)
            {
                win.Activated -= OnWindowActivated;
                if (e.NewValue != null)
                    win.Activated += OnWindowActivated;
            }
        }

        /// <summary>
        /// This is the handler for the Activated event to raise the command.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private static void OnWindowActivated(object sender, EventArgs e)
        {
            var dpo = (DependencyObject)sender;
            ICommand activatedCommand = GetActivated(dpo);
            if (activatedCommand != null)
                activatedCommand.Execute(GetCommandParameter(dpo));
        }
        #endregion
    }
}

The same mechanism is used for:

  • View Deactivated event, where the SimpleCommand in the Cinch ViewModelBase is called DeactivatedCommand, and the virtual method in the Cinch ViewModelBase is called OnWindowDeactivated()
  • View Closing/Closed event, where the SimpleCommand in the Cinch ViewModelBase is called Closeommand, and the virtual method in the Cinch ViewModelBase is called OnWindowClose()
  • View Loaded event, where the SimpleCommand in the Cinch ViewModelBase is called LoadedCommand, and the virtual method in the Cinch ViewModelBase is called OnWindowLoaded()

Again, don't forget there is no standard implementation provided by the Cinch ViewModelBase as this functionality should be performed by overriding these methods in a ViewModel which inherits from the Cinch ViewModelBase.

So all you have to do is inherit from the Cinch ViewModelBase, and you can use these View lifecycle events/properties, just remember to call the base methods.

Numeric Textbox Attached Behaviour

One very common requirement when developing LOB applications is to have numeric only data entry on textboxes. Whilst this is possible to achieve using Regular Expressions and some code-behind and possibly a ValidationRule, wouldn't it just be easier to not let the textbox accept anything apart from numbers in the first place? To this end, Cinch contains a NumericTextBoxBehavior attached behaviour which looks like below. You should also note that this behaviour caters for pasted text using the DataObject pasting event.

C#
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System;
using System.Linq;
using System.Windows.Controls;
using System.Text.RegularExpressions;

/// <summary>
/// This forces a TextBoxBase control to be numeric-entry only
/// By using an attached behaviour
/// </summary>
namespace Cinch
{
    /// <summary>
    /// This forces a TextBoxBase control to be numeric-entry only
    /// </summary>
    /// <example>
    /// <![CDATA[  <TextBox Cinch:NumericTextBoxBehavior.IsEnabled="True" />  ]]>
    /// </example>
    public static class NumericTextBoxBehavior
    {
        #region IsEnabled DP
        /// <summary>
        /// Dependency Property for turning on numeric behavior in a TextBox.
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled",
                typeof(bool), typeof(NumericTextBoxBehavior),
                    new UIPropertyMetadata(false, OnEnabledStateChanged));

        /// <summary>
        /// Attached Property getter for the IsEnabled property.
        /// </summary>
        /// <param name="source">Dependency Object</param>
        /// <returns>Current property value</returns>
        public static bool GetIsEnabled(DependencyObject source)
        {
            return (bool)source.GetValue(IsEnabledProperty);
        }

        /// <summary>
        /// Attached Property setter for the IsEnabled property.
        /// </summary>
        /// <param name="source">Dependency Object</param>
        /// <param name="value">Value to set on the object</param>
        public static void SetIsEnabled(DependencyObject source, bool value)
        {
            source.SetValue(IsEnabledProperty, value);
        }

        /// <summary>
        /// This is the property changed handler for the IsEnabled property.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void OnEnabledStateChanged(DependencyObject sender,
            DependencyPropertyChangedEventArgs e)
        {
            TextBox tb = sender as TextBox;
            if (tb == null)
                return;

            tb.PreviewTextInput -= tbb_PreviewTextInput;
            DataObject.RemovePastingHandler(tb, OnClipboardPaste);

            bool b = ((e.NewValue != null && e.NewValue.GetType() == typeof(bool))) ?
                (bool)e.NewValue : false;
            if (b)
            {
                tb.PreviewTextInput += tbb_PreviewTextInput;
                DataObject.AddPastingHandler(tb, OnClipboardPaste);
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// This method handles paste and drag/drop events
        /// onto the TextBox. It restricts the character
        /// set to numerics and ensures we have consistent behavior.
        /// </summary>
        /// <param name="sender">TextBox sender</param>
        /// <param name="e">EventArgs</param>
        private static void OnClipboardPaste(object sender, DataObjectPastingEventArgs e)
        {
            TextBox tb = sender as TextBox;
            string text = e.SourceDataObject.GetData(e.FormatToApply) as string;

            if (tb != null && !string.IsNullOrEmpty(text) && !Validate(tb, text))
                e.CancelCommand();
        }

        /// <summary>
        /// This checks if the resulting string will match the regex expression
        /// </summary>
        static void tbb_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            TextBox tb = sender as TextBox;

            if (tb != null && !Validate(tb, e.Text))
                e.Handled = true;
        }

        #endregion

        private static bool Validate(TextBox tb, string newContent)
        {
            string testString = string.Empty;
            // replace selection with new text.
            if (!string.IsNullOrEmpty(tb.SelectedText))
            {
                string pre = tb.Text.Substring(0, tb.SelectionStart);
                string after = tb.Text.Substring(tb.SelectionStart + tb.SelectionLength, 
                               tb.Text.Length - (tb.SelectionStart + tb.SelectionLength));
                testString = pre + newContent + after;
            }
            else
            {
                string pre = tb.Text.Substring(0, tb.CaretIndex);
                string after = tb.Text.Substring(tb.CaretIndex, 
                                       tb.Text.Length - tb.CaretIndex);
                testString = pre + newContent + after;
            }

            Regex regExpr = new Regex(@"^([-+]?)(\d*)([,.]?)(\d*)$");
            if (regExpr.IsMatch(testString))
                return true;

            return false;
        }
    }
}

Which can easily be applied to a textbox in your application as follows:

XML
<Window x:Class="MVVM.Demo.Views.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Cinch="clr-namespace:Cinch;assembly=Cinch"
    Title="Window1" Height="300" Width="300">

    <Grid>
        <TextBox Cinch:NumericTextBoxBehavior.IsEnabled="True" />
    </Grid>
</Window>

With this attached behaviour enabled, only numeric data entry will be allowed for the textbox that has the NumericTextBoxBehavior attached behaviour applied.

Attached Command Behaviour

Continuing with the attached behaviors, it is also sometimes very convenient to be able to fire a ViewModel ICommand from a FrameworkElement RoutedEvent. This is something that the WPF framework does not do out of the box, though Blend 3 does allow this by the use of the Blend Interactivity DLL. If you want an example of that, have a look at my blog post WPF: Blend 3 Interactions / Behaviours.

But we are where we are, and I want people to know how to do this without using a non released DLL from a different product that will more than likely find its way into WPF proper at some stage. So let's continue with how to do it without the Blend Interactivity DLL.

So here is how, Cinch actually provides two alternatives here, attaching a single ICommand to a single FrameworkElement RoutedEvent, or attaching a collection of ICommands to a FrameworkElement using different RoutedEvents.

Let's start simple, and build up from there.

Attaching a single ICommand to a single FrameworkElement RoutedEvent

This is easily done via yet another attached property or two, which you can set on a View FrameworkElement like this:

XML
<Window x:Class="MVVM.Demo.Views.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Cinch="clr-namespace:Cinch;assembly=Cinch"
    Title="Window1" Height="300" Width="300">

     <Grid Background="WhiteSmoke"
           Cinch:SingleEventCommand.RoutedEventName="MouseDown"
          Cinch:SingleEventCommand.TheCommandToRun=
           "{Binding Path=ShowWindowCommand}"/>
</Window>

So we set the RoutedEvent to fire the ICommand and we set the ViewModel ICommand to execute via a Binding.

So all that is left is to look at how this is achieved, which is done with a standard DP or two and a touch of Reflection to get the RoutedEvent from the DependencyObject that declares the RoutedEventName attached DP, and from there, we just create a Delegate which is called when the event is raised, which in turn fires the ICommand that was specified using the TheCommandToRun DP.

Here is the code:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Reflection;
using System.Windows.Media;

namespace Cinch
{
    #region SCommandArgs Class
    /// <summary>
    /// Allows a CommandParameter to be associated with a SingleEventCommand
    /// </summary>
    public class SCommandArgs
    {
        #region Data
        public object Sender { get; set; }
        public object EventArgs { get; set; }
        public object CommandParameter { get; set; }
        #endregion

        #region Ctor
        public SCommandArgs()
        {
        }

        public SCommandArgs(object sender, object eventArgs, 
                            object commandParameter)
        {
            Sender = sender;
            EventArgs = eventArgs;
            CommandParameter = commandParameter;
        }
        #endregion
    }
    #endregion

    #region SingleEventCommand Class
    /// <summary>
    /// This class allows a single command to event mappings. 
    /// It is used to wire up View events to a
    /// ViewModel ICommand implementation. 
    /// </summary>
    /// <example>
    /// <![CDATA[
    /// 
    /// <ListBox ...    
    /// Cinch:SingleEventCommand.RoutedEventName="SelectionChanged"     
    /// Cinch:SingleEventCommand.TheCommandToRun="{Binding Path=BoxEditCommand}"     
    /// Cinch:SingleEventCommand.CommandParameter=
    ///         "{Binding ElementName=ListBoxVehicle, Path=SelectedItem}">
    /// </ListBox>
    /// 
    /// ]]>
    /// </example>
    public static class SingleEventCommand
    {
        #region TheCommandToRun

        /// <summary>
        /// TheCommandToRun : The actual ICommand to run
        /// </summary>
        public static readonly DependencyProperty TheCommandToRunProperty =
            DependencyProperty.RegisterAttached("TheCommandToRun",
                typeof(ICommand),
                typeof(SingleEventCommand),
                new FrameworkPropertyMetadata((ICommand)null));

        /// <summary>
        /// Gets the TheCommandToRun property. 
        /// </summary>
        public static ICommand GetTheCommandToRun(DependencyObject d)
        {
            return (ICommand)d.GetValue(TheCommandToRunProperty);
        }

        /// <summary>
        /// Sets the TheCommandToRun property. 
        /// </summary>
        public static void SetTheCommandToRun(DependencyObject d, ICommand value)
        {
            d.SetValue(TheCommandToRunProperty, value);
        }
        #endregion

        #region RoutedEventName

        /// <summary>
        /// RoutedEventName : The event that should actually execute the
        /// ICommand
        /// </summary>
        public static readonly DependencyProperty RoutedEventNameProperty =
            DependencyProperty.RegisterAttached("RoutedEventName", typeof(String),
            typeof(SingleEventCommand),
                new FrameworkPropertyMetadata((String)String.Empty,
                    new PropertyChangedCallback(OnRoutedEventNameChanged)));

        /// <summary>
        /// Gets the RoutedEventName property. 
        /// </summary>
        public static String GetRoutedEventName(DependencyObject d)
        {
            return (String)d.GetValue(RoutedEventNameProperty);
        }

        /// <summary>
        /// Sets the RoutedEventName property. 
        /// </summary>
        public static void SetRoutedEventName(DependencyObject d, String value)
        {
            d.SetValue(RoutedEventNameProperty, value);
        }

        /// <summary>
        /// Hooks up a Dynamically created EventHandler (by using the 
        /// <see cref="EventHooker">EventHooker</see> class) that when
        /// run will run the associated ICommand
        /// </summary>
        private static void OnRoutedEventNameChanged(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            String routedEvent = (String)e.NewValue;

            if (d == null || String.IsNullOrEmpty(routedEvent))
                return;


            //If the RoutedEvent string is not null, create a new
            //dynamically created EventHandler that when run will execute
            //the actual bound ICommand instance (usually in the ViewModel)
            EventHooker eventHooker = new EventHooker();
            eventHooker.ObjectWithAttachedCommand = d;

            EventInfo eventInfo = d.GetType().GetEvent(routedEvent,
                BindingFlags.Public | BindingFlags.Instance);

            //Hook up Dynamically created event handler
            if (eventInfo != null)
            {
                eventInfo.RemoveEventHandler(d,
                    eventHooker.GetNewEventHandlerToRunCommand(eventInfo));

                eventInfo.AddEventHandler(d,
                    eventHooker.GetNewEventHandlerToRunCommand(eventInfo));
            }

        }
        #endregion

        #region CommandParameter
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached("CommandParameter", typeof(object),
            typeof(SingleEventCommand), new UIPropertyMetadata(null));

        /// <summary>        
        /// Gets the CommandParameter property.         
        /// </summary>        
        public static object GetCommandParameter(DependencyObject obj)
        {
            return (object)obj.GetValue(CommandParameterProperty);
        }


        /// <summary>        
        /// Sets the CommandParameter property.         
        /// </summary>        
        public static void SetCommandParameter(DependencyObject obj, object value)
        {
            obj.SetValue(CommandParameterProperty, value);
        }
        #endregion

    }
    #endregion

    #region EventHooker Class
    /// <summary>
    /// Contains the event that is hooked into the source RoutedEvent
    /// that was specified to run the ICommand
    /// </summary>
    sealed class EventHooker
    {
        #region Public Methods/Properties
        /// <summary>
        /// The DependencyObject, that holds a binding to the actual
        /// ICommand to execute
        /// </summary>
        public DependencyObject ObjectWithAttachedCommand { get; set; }

        /// <summary>
        /// Creates a Dynamic EventHandler that will be run the ICommand
        /// when the user specified RoutedEvent fires
        /// </summary>
        /// <param name="eventInfo">The specified RoutedEvent EventInfo</param>
        /// <returns>An Delegate that points to a new EventHandler
        /// that will be run the ICommand</returns>
        public Delegate GetNewEventHandlerToRunCommand(EventInfo eventInfo)
        {
            Delegate del = null;

            if (eventInfo == null)
                throw new ArgumentNullException("eventInfo");

            if (eventInfo.EventHandlerType == null)
                throw new ArgumentException("EventHandlerType is null");

            if (del == null)
                del = Delegate.CreateDelegate(eventInfo.EventHandlerType, this,
                      GetType().GetMethod("OnEventRaised",
                        BindingFlags.NonPublic |
                        BindingFlags.Instance));

            return del;
        }
        #endregion

        #region Private Methods

        /// <summary>
        /// Runs the ICommand when the requested RoutedEvent fires
        /// </summary>
        private void OnEventRaised(object sender, EventArgs e)
        {
            ICommand command = (ICommand)(sender as DependencyObject).
                GetValue(SingleEventCommand.TheCommandToRunProperty);

            object commandParameter = (sender as DependencyObject).
                GetValue(SingleEventCommand.CommandParameterProperty);

            SCommandArgs commandArgs = new SCommandArgs(sender, e, commandParameter);
            if (command != null)
                command.Execute(commandArgs);

        }
        #endregion
    }
    #endregion

}

This allows a single FrameworkElement RoutedEvent to fire a single ViewModel ICommand and pass down the original EventArgs/Sender and CommandParamtere down to the ViewModel all within the SCommandArgs object. So that means, all you have to do within your ViewModel to use the original EventArgs is something like this:

C#
someCommand= new SimpleCommand
{
    CanExecuteDelegate = x => true,
    ExecuteDelegate = x => ExecuteSomeCommand(x)
};

private void ExecuteSomeCommand(Object o)
{
    //if using SingleEventCommand
    Cinch.SCommandArgs data =(Cinch.SCommandArgs)o;

    //now you have access to
    //data.Sender
    //data.EventArgs
    //data.CommandParameter

}

But Cinch does more than that. So let's look at the more advanced scenario.

Attaching a collection of ICommand(s) to a FrameworkElement using different RoutedEvent(s)

As before, let us see how you would use the attached collection of ICommands/RoutedEvents from a View. Here is how you might do that:

XML
<Window x:Class="MVVM.Demo.Views.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Cinch="clr-namespace:Cinch;assembly=Cinch"
    Title="Window1" Height="300" Width="300">

    <Grid Background="WhiteSmoke">
       <Behaviors:EventCommander.Mappings>

          <Behaviors:CommandEvent 
              Command="{Binding MouseEnterCommand}" 
              Event="MouseEnter" />

          <Behaviors:CommandEvent 
              Command="{Binding MouseLeaveCommand}" 
              Event="MouseLeave" />

       </Behaviors:EventCommander.Mappings>
    </Grid>

</Window>

So you can see that this time, there is an attached property (EventCommander.Mappings) that is expecting a collection of CommandEvent objects. The concept behind this is largely the same, except this time, there is a collection of these CommandEvent objects, but they largely work the same as just described above, so I will concentrate on how the EventCommander.Mappings collection works, and assume the discussion above was enough to explain a single CommandEvent object.

The EventCommander.Mappings collection looks like this:

C#
public static class EventCommander
{
    #region InternalMappings DP
    // Make it internal so WPF ignores the property and always uses the 
    //public getter/setter. This is per John Gossman blog post - 07/2008.
    internal static readonly DependencyProperty MappingsProperty = 
        DependencyProperty.RegisterAttached("InternalMappings", 
                        typeof(CommandEventCollection), typeof(EventCommander),
                        new UIPropertyMetadata(null, OnMappingsChanged));

    /// <summary>
    /// Retrieves the mapping collection
    /// </summary>
    /// <param name="obj"></param>

    /// <returns></returns>
    internal static CommandEventCollection InternalGetMappingCollection(
        DependencyObject obj)
    {
        var map = obj.GetValue(MappingsProperty) as CommandEventCollection;
        if (map == null)
        {
            map = new CommandEventCollection();
            SetMappings(obj, map);
        }
        return map;
    }

    /// <summary>
    /// This retrieves the mapping collection
    /// </summary>
    /// <param name="obj">Dependency Object</param>

    /// <returns>Mapping collection</returns>
    public static IList GetMappings(DependencyObject obj)
    {
        return InternalGetMappingCollection(obj);
    }

    /// <summary>
    /// This sets the mapping collection.
    /// </summary>
    /// <param name="obj">Dependency Object</param>

    /// <param name="value">Mapping collection</param>
    public static void SetMappings(DependencyObject obj, 
        CommandEventCollection value)
    {
        obj.SetValue(MappingsProperty, value);
    }

    /// <summary>
    /// This changes the event mapping
    /// </summary>
    /// <param name="target"></param>

    /// <param name="e"></param>
    private static void OnMappingsChanged(DependencyObject target, 
        DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue != null)
        {
            CommandEventCollection cec = e.OldValue as CommandEventCollection;
            if (cec != null)
                cec.Unsubscribe(target);
        }
        if (e.NewValue != null)
        {
            CommandEventCollection cec = e.NewValue as CommandEventCollection;
            if (cec != null)
                cec.Subscribe(target);
        }
    }
    #endregion
}

Where it can be seen that behind the scenes, the EventCommander.Mappings collection is populating a CommandEventCollection. The CommandEventCollection inherits from Freezable, as this is a trick you can use to get DataContext inheritance. Basically, what happens is that by inheriting from Freezable, a non-UI element will also get the current UI element's DataContext (which is more than likely the ViewModel). If you did not inherit from Freezable, the CommandEventCollection would not be able to pick up the ViewModels bound ICommand; as it would know nothing about a current DataContext object, it would be null. It's a trick, but it works. If you want to know more, read Mike Hillberg's blog entry: Mike Hillberg's Freezable blog post, and maybe have a look at Josh Smith's DataContextSpy post, which is also very useful.

Anyway, the CommandEventCollection looks like this, where its main job is to maintain the list of current CommandEvents:

C#
public class CommandEventCollection : FreezableCollection<CommandEvent>
{
    #region Data
    private object _target;
    private readonly List<CommandEvent> _currentList = new List<CommandEvent>();
    #endregion

    #region Ctor
    /// <summary>

    /// Constructor
    /// </summary>
    public CommandEventCollection()
    {
        ((INotifyCollectionChanged)this).CollectionChanged += 
                                         OnCollectionChanged;
    }
    #endregion

    #region Private/Internal Methods
    /// <summary>
    /// Wire up events to the target
    /// </summary>
    /// <param name="target"></param>

    internal void Subscribe(object target)
    {
        _target = target;
        foreach(var item in this)
            item.Subscribe(target);
    }

    /// <summary>
    /// Unwire all target events
    /// </summary>
    /// <param name="target"></param>
    internal void Unsubscribe(object target)
    {
        foreach (var item in this)
            item.Unsubscribe(target);
        _target = null;
    }

    /// <summary>

    /// This handles the collection change event - it then
    /// subscribes and unsubscribes events.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>

    private void OnCollectionChanged(object sender, 
                 NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var item in e.NewItems)
                    OnItemAdded((CommandEvent)item);
                break;

            case NotifyCollectionChangedAction.Remove:
                foreach (var item in e.OldItems)
                    OnItemRemoved((CommandEvent)item);
                break;

            case NotifyCollectionChangedAction.Replace:
                foreach (var item in e.OldItems)
                    OnItemRemoved((CommandEvent)item);
                foreach (var item in e.NewItems)
                    OnItemAdded((CommandEvent)item);
                break;

            case NotifyCollectionChangedAction.Move:
                break;

            case NotifyCollectionChangedAction.Reset:
                _currentList.ForEach(i => i.Unsubscribe(_target));
                _currentList.Clear();
                foreach (var item in this)
                    OnItemAdded(item);
                break;

            default:
                return;
        }
    }

    /// <summary>

    /// A new item has been added to the event list
    /// </summary>
    /// <param name="item"></param>

    private void OnItemAdded(CommandEvent item)
    {
        if (item != null && _target != null)
        {
            _currentList.Add(item);
            item.Subscribe(_target);                
        }
    }

    /// <summary>
    /// An item has been removed from the event list.
    /// </summary>

    /// <param name="item"></param>

    private void OnItemRemoved(CommandEvent item)
    {
        if (item != null && _target != null)
        {
            _currentList.Remove(item);
            item.Unsubscribe(_target);
        }
    }
    #endregion
}

As before, to use the original EventArgs in the ViewModel ICommand, we just need to do something like this in the ViewModel:

C#
someCommand= new SimpleCommand
{
    CanExecuteDelegate = x => true,
    ExecuteDelegate = x => ExecuteSomeCommand(x)
};


private void ExecuteSomeCommand(Object o)
{
    //if using EventCommander
    Cinch.EventParameters data =(Cinch.EventParameters)o;

    //now you have access to
    //data.Sender
    //data.EventArgs
}

Better INPC, No Magic Strings

When working with WPF, it is very common to have your ViewModel/Model classes implement the System.ComponentModel.INotifyPropertyChanged interface for binding notification of property changes. This is typically implemented like this:

C#
using System.ComponentModel;

namespace SDKSample
{
  // This class implements INotifyPropertyChanged
  // to support one-way and two-way bindings
  // (such that the UI element updates when the source
  // has been changed dynamically)
  public class Person : INotifyPropertyChanged
  {
      private string name;
      // Declare the event
      public event PropertyChangedEventHandler PropertyChanged;

      public Person()
      {
      }

      public Person(string value)
      {
          this.name = value;
      }

      public string PersonName
      {
          get { return name; }
          set
          {
              name = value;
              // Call OnPropertyChanged whenever the property is updated
              OnPropertyChanged("PersonName");
          }
      }

      // Create the OnPropertyChanged method to raise the event
      protected void OnPropertyChanged(string name)
      {
          PropertyChangedEventHandler handler = PropertyChanged;
          if (handler != null)
          {
              handler(this, new PropertyChangedEventArgs(name));
          }
      }
  }
}

The problem with this approach is that the code has magic strings in it which are easy to get wrong. Have a look at the OnPropertyChanged("PersonName"); code shown above. I had originally been using some static Reflection by using LINQ expression trees to obtain the property name. I was using Bill Kempf's excellent Reflect code. The problem with the static reflection Expression tree thing is that it is slow to create the eventArgs each time. One of the readers of this article actually came up with a better solution where the INPC EventArgs are created once and then re-used. I liked this idea a lot, so have now included it in Cinch. The reader's post is available here if you want some more information about this: CinchII.aspx?msg=3141144#xx3141144xx.

Cinch now uses this approach. Here is how it works. There is a static helper class which looks like this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;

namespace Cinch
{
    /// <summary>
    /// A small helper class that has a method to help create
    /// PropertyChangedEventArgs when using the INotifyPropertyChanged
    /// interface
    /// </summary>
    public static class ObservableHelper
    {
        #region Public Methods
        /// <summary>
        /// Creates PropertyChangedEventArgs
        /// </summary>
        /// <param name="propertyExpression">Expression to make 
        /// PropertyChangedEventArgs out of</param>
        /// <returns>PropertyChangedEventArgs</returns>
        public static PropertyChangedEventArgs CreateArgs<T>(
            Expression<Func<T, Object>> propertyExpression)
        {
            return new PropertyChangedEventArgs(
                GetPropertyName<T>(propertyExpression));
        }

        /// <summary>
        /// Creates PropertyChangedEventArgs
        /// </summary>
        /// <param name="propertyExpression">Expression to make 
        /// PropertyChangedEventArgs out of</param>
        /// <returns>PropertyChangedEventArgs</returns>
        public static string GetPropertyName<T>(
            Expression<Func<T, Object>> propertyExpression)
        {
            var lambda = propertyExpression as LambdaExpression;
            MemberExpression memberExpression;
            if (lambda.Body is UnaryExpression)
            {
                var unaryExpression = lambda.Body as UnaryExpression;
                memberExpression = unaryExpression.Operand as MemberExpression;
            }
            else
            {
                memberExpression = lambda.Body as MemberExpression;
            }

            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

        #endregion
    }
}

Which we can then use like this from any property:

C#
static PropertyChangedEventArgs currentCustomerChangeArgs =
    ObservableHelper.CreateArgs<AddEditOrderViewModel>(x => x.CurrentCustomer);

public CustomerModel CurrentCustomer
{
    get { return currentCustomer; }
    set
    {
        currentCustomer = value;
        NotifyPropertyChanged(currentCustomerChangeArgs);
    }
}

No more magic strings, at all. Most excellent.

ViewModel Modes

One of the things I have always struggled with when working with MVVM and using it to produce LOB apps is View mode. For example, it would be nice to have a View that is read only and then the user clicks edit and then all the fields on the View are editable. Now this could be achieved by having a command in the ViewModel that changes from ReadOnly mode to EditMode say, and all the UIElements on the View could bind to some CurrentMode property on the ViewModel. Sounds do'able, but as we all know, things are never as clean cut as that. In my workplace, we have complicated requirements around data entry and there is no way a single mode can be applied to all data entry fields on a single View. No way, we need very granular data entry rights, down to the individual field level.

So this got me thinking. What we need is an editable state for each data item in a UI Model. I thought about this some more and came up with a generic wrapper class which wraps a single property but also exposes an IsEditable property. Now the ViewModel can access these wrappers as they are public properties, on either UI Model classes, or actually public properties in the ViewModel (I expose a CurrentX object of my ViewModel, but others repeat all the UI Model properties in the ViewModel; me, I have no issues with writing straight from the View to the Model, providing it doesn't get to the database if its InValid), so it can bind its data to the wrapper's data property and can disable data entry based on the wrapper's IsEditable property.

To this end, I came up with a simple class that looks like this:

C#
using System;
using System.Reflection;
using System.Diagnostics;
using System.Linq;
using System.ComponentModel;

using System.Collections.Generic;

namespace Cinch
{
    /// <summary>
    /// Abstract base class for DataWrapper which
    /// will support IsDirty. So in your ViewModel
    /// you could do something like
    /// 
    /// <example>
    /// <![CDATA[
    /// 
    ///public bool IsDirty
    ///{
    ///   get
    ///   {
    ///     return cachedListOfDataWrappers.Where(x => (x is IChangeIndicator)
    ///     &&  ((IChangeIndicator)x).IsDirty).Count() > 0;
    ///   }
    ///
    /// } 
    /// ]]>
    /// </example>
    /// </summary>
    public abstract class DataWrapperDirtySupportingBase : EditableValidatingObject
    {
        #region Public Properties
        /// <summary>
        /// Deteremines if a property has changes since is was put into edit mode
        /// </summary>
        /// <param name="propertyName">The property name</param>
        /// <returns>True if the property has changes
        ///    since is was put into edit mode</returns>
        public bool HasPropertyChanged(string propertyName)
        {
            if (_savedState == null)
                return false;

            object saveValue;
            object currentValue;
            if (!_savedState.TryGetValue(propertyName, out saveValue) ||
                  !this.GetFieldValues().TryGetValue(propertyName, out currentValue))
                return false;
            if (saveValue == null || currentValue == null)
                return saveValue != currentValue;

            return !saveValue.Equals(currentValue);
        }
        #endregion
    }

    /// <summary>
    /// Abstract base class for DataWrapper - allows easier access to
    /// methods for the DataWrapperHelper.
    /// </summary>
    public abstract class DataWrapperBase : DataWrapperDirtySupportingBase
    {
        #region Data
        private Boolean isEditable = false;

        private IParentablePropertyExposer parent = null;
        private PropertyChangedEventArgs parentPropertyChangeArgs = null;
        #endregion

        #region Ctors
        public DataWrapperBase()
        {
        }

        public DataWrapperBase(IParentablePropertyExposer parent,
            PropertyChangedEventArgs parentPropertyChangeArgs)
        {
            this.parent = parent;
            this.parentPropertyChangeArgs = parentPropertyChangeArgs;
        }
        #endregion

        #region Protected Methods

        /// <summary>
        /// Notifies all the parent (INPC) objects
        /// INotifyPropertyChanged.PropertyChanged subscribed delegates
        /// that an internal DataWrapper property value
        /// has changed, which in turn raises the appropriate
        /// INotifyPropertyChanged.PropertyChanged event on the parent (INPC) object
        /// </summary>
        protected internal void NotifyParentPropertyChanged()
        {
            if (parent == null || parentPropertyChangeArgs == null)
                return;

            //notify all delegates listening to DataWrapper<T> 
            //       parent objects PropertyChanged event
            Delegate[] subscribers = parent.GetINPCSubscribers();
            if (subscribers != null)
            {
                foreach (PropertyChangedEventHandler d in subscribers)
                {
                    d(parent, parentPropertyChangeArgs);
                }
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// The editable state of the data, the View
        /// is expected to use this to enable/disable
        /// data entry. The ViewModel would set this
        /// property
        /// </summary>
        static PropertyChangedEventArgs isEditableChangeArgs =
            ObservableHelper.CreateArgs<DataWrapperBase>(x => x.IsEditable);

        public Boolean IsEditable
        {
            get { return isEditable; }
            set
            {
                if (isEditable != value)
                {
                    isEditable = value;
                    NotifyPropertyChanged(isEditableChangeArgs);
                    NotifyParentPropertyChanged();
                }
            }
        }
        #endregion
    }

    /// <summary>
    /// This interface is here so to ensure that both DataWrapper of T
    /// and DataWrapperExt of T have a commonly named property for
    /// the data (DataValue) and that we can safely retrieve this
    /// name elsewhere via static reflection.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IDataWrapper<T>
    {
        T DataValue { get; set; }
    }

    /// <summary>
    /// Allows IsDierty to be determined for a cached list of DataWrappers
    /// </summary>
    public interface IChangeIndicator
    {
        bool IsDirty { get; }
    }

    /// <summary>
    /// This interface is implemented by both the 
    /// <see cref="ValidatingObject">ValidatingObject</see> and the
    /// <see cref="ViewModelBase">ViewModelBase</see> classes, and is used
    /// to expose the list of delegates that are currently listening to the
    /// <see cref="System.ComponentModel.INotifyPropertyChanged">INotifyPropertyChanged</see>
    /// PropertyChanged event. This is done so that the internal 
    /// <see cref="DataWrapper">DataWrapper</see> classes can notify their parent object
    /// when an internal <see cref="DataWrapper">DataWrapper</see> property changes
    /// </summary>
    public interface IParentablePropertyExposer
    {
        Delegate[] GetINPCSubscribers();
    }

    /// <summary>
    /// Provides a wrapper around a single piece of data
    /// such that the ViewModel can put the data item
    /// into a editable state and the View can bind to
    /// both the DataValue for the actual Value, and to 
    /// the IsEditable to determine if the control which
    /// has the data is allowed to be used for entering data.
    /// 
    /// The Viewmodel is expected to set the state of the
    /// IsEditable property for all DataWrappers in a given Model
    /// </summary>
    /// <typeparam name="T">The type of the Data</typeparam>
    public class DataWrapper<T> : DataWrapperBase, 
                 IDataWrapper<T>, IChangeIndicator
    {
        #region Data
        private T dataValue = default(T);
        private bool isDirty = false;
        #endregion

        #region Ctors
        public DataWrapper()
        {
        }

        public DataWrapper(T initialValue)
        {
            dataValue = initialValue;
        }

        public DataWrapper(IParentablePropertyExposer parent,
            PropertyChangedEventArgs parentPropertyChangeArgs)
            : base(parent, parentPropertyChangeArgs)
        {
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// The actual data value, the View is
        /// expected to bind to this to display data
        /// </summary>
        static PropertyChangedEventArgs dataValueChangeArgs =
            ObservableHelper.CreateArgs<DataWrapper<T>>(x => x.DataValue);

        public T DataValue
        {
            get { return dataValue; }
            set
            {
                dataValue = value;
                NotifyPropertyChanged(dataValueChangeArgs);
                NotifyParentPropertyChanged();
                IsDirty = this.HasPropertyChanged("dataValue"); 
            }
        }


        /// <summary>
        /// The IsDirty status of this DataWrapper
        /// </summary>
        static PropertyChangedEventArgs isDirtyChangeArgs =
            ObservableHelper.CreateArgs<DataWrapper<T>>(x => x.IsDirty);

        public bool IsDirty
        {
            get { return isDirty; }
            set
            {
                isDirty = value;
                NotifyPropertyChanged(isDirtyChangeArgs);
                NotifyParentPropertyChanged();
            }
        }
        #endregion
    }

    /// <summary>
    /// Provides helper methods for dealing with DataWrappers
    /// within the Cinch library. 
    /// </summary>
    public class DataWrapperHelper
    {
        #region Public Methods
        // The following functions may be used when dealing with model/viewmodel objects
        // whose entire set of DataWrapper properties are immutable (only have a getter
        // for the property). They avoid having to do reflection to retrieve the list
        // of wrapper properties every time a mode change, edit state change

        /// <summary>
        /// Set all Cinch.DataWrapper properties to have
        /// the correct Cinch.DataWrapper.IsEditable 
        /// to the correct state based on the current ViewMode 
        /// </summary>
        /// <param name="wrapperProperties">The properties
        /// on which to change the mode</param>
        /// <param name="currentViewMode">The current ViewMode</param
        public static void SetMode(IEnumerable<DataWrapperBase> wrapperProperties,
            ViewMode currentViewMode)
        {
            bool isEditable = currentViewMode ==
                    ViewMode.EditMode || currentViewMode == ViewMode.AddMode;

            foreach (var wrapperProperty in wrapperProperties)
            {
                try
                {
                    wrapperProperty.IsEditable = isEditable;
                }
                catch (Exception)
                {
                    Debug.WriteLine("There was a problem setting the currentViewMode");
                }
            }
        }

        /// <summary>
        /// Loops through a source object (UI Model class is expected really) and attempts
        /// to call the BeginEdit() method of all the  Cinch.DataWrapper fields
        /// </summary>
        /// <param name="wrapperProperties">The DataWrapperBase objects</param>
        public static void SetBeginEdit(IEnumerable<DataWrapperBase> wrapperProperties)
        {
            foreach (var wrapperProperty in wrapperProperties)
            {
                try
                {
                    wrapperProperty.BeginEdit();
                    wrapperProperty.NotifyParentPropertyChanged();
                }
                catch (Exception)
                {
                    Debug.WriteLine("There was a problem calling the " + 
                          "BeginEdit method for the current DataWrapper");
                }
            }
        }

        /// <summary>
        /// Loops through a source object (UI Model class is expected really) and attempts
        /// to call the CancelEdit() method of all the  Cinch.DataWrapper fields
        /// </summary>
        /// <param name="wrapperProperties">The DataWrapperBase objects</param>
        public static void SetCancelEdit(IEnumerable<DataWrapperBase> wrapperProperties)
        {
            foreach (var wrapperProperty in wrapperProperties)
            {
                try
                {
                    wrapperProperty.CancelEdit();
                    wrapperProperty.NotifyParentPropertyChanged();
                }
                catch (Exception)
                {
                    Debug.WriteLine("There was a problem calling " + 
                          "the CancelEdit method for the current DataWrapper");
                }
            }
        }

        /// <summary>
        /// Loops through a source object (UI Model class is expected really) and attempts
        /// to call the EditEdit() method of all the  Cinch.DataWrapper fields
        /// </summary>
        /// <param name="wrapperProperties">The DataWrapperBase objects</param>
        public static void SetEndEdit(IEnumerable<DataWrapperBase> wrapperProperties)
        {
            foreach (var wrapperProperty in wrapperProperties)
            {
                try
                {
                    wrapperProperty.EndEdit();
                    wrapperProperty.NotifyParentPropertyChanged();
                }
                catch (Exception)
                {
                    Debug.WriteLine("There was a problem calling " + 
                          "the EndEdit method for the current DataWrapper");
                }
            }
        }

        /// <summary>
        /// Loops through a source object (UI Model
        /// class is expected really) and attempts
        /// to call the EditEdit() method of all the  Cinch.DataWrapper fields
        /// </summary>
        /// <param name="wrapperProperties">The DataWrapperBase objects</param>
        public static Boolean AllValid(IEnumerable<DataWrapperBase> wrapperProperties)
        {
            Boolean allValid = true;

            foreach (var wrapperProperty in wrapperProperties)
            {
                try
                {
                    allValid &= wrapperProperty.IsValid;
                    if (!allValid)
                        break;
                }
                catch (Exception)
                {
                    allValid = false;
                    Debug.WriteLine("There was a problem calling " + 
                          "the IsValid method for the current DataWrapper");
                }
            }

            return allValid;
        }

        /// <summary>
        /// Get a list of the wrapper properties on the parent object.
        /// </summary>
        /// <typeparam name="T">The type of object</typeparam>
        /// <param name="parentObject">The parent object to examine</param>
        /// <returns>A IEnumerable of DataWrapperBase</returns>
        public static IEnumerable<DataWrapperBase> GetWrapperProperties<T>(T parentObject)
        {
            var properties = parentObject.GetType().GetProperties(
                BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            List<DataWrapperBase> wrapperProperties = new List<DataWrapperBase>();

            foreach (var propItem in parentObject.GetType().GetProperties(
                           BindingFlags.Public | 
                           BindingFlags.NonPublic | BindingFlags.Instance))
            {
                // check make sure can read and that
                // the property is not an indexed property
                if (propItem.CanRead && propItem.GetIndexParameters().Count() == 0)
                {
                    // we ignore any property whose
                    // type CANNOT store a DataWrapper;
                    // this means any property whose type
                    // is not in the inheritance hierarchy
                    // of DataWrapper. For example a property
                    // of type Object could potentially
                    // store a DataWrapper since Object
                    // is in DataWrapper's inheritance tree.
                    // However, a boolean property CANNOT
                    // since it's not in the wrapper's
                    // inheritance tree.
                    if (typeof(DataWrapperBase).IsAssignableFrom(
                                  propItem.PropertyType) == false)
                        continue;

                    // make sure properties value is not null ref
                    var propertyValue = propItem.GetValue(parentObject, null);
                    if (propertyValue != null && propertyValue is DataWrapperBase)
                    {
                        wrapperProperties.Add((DataWrapperBase)propertyValue);
                    }
                }
            }

            return wrapperProperties;
        }
        #endregion
    }
}

Image 1

Which can then be used as properties on your UI Model classes (or directly on the ViewModel if you want to), like this (don't worry about the inheriting from Cinch.EditableValidatingObject, we'll get to that soon):

C#
public class OrderModel : Cinch.EditableValidatingObject
{
    //Any data item is declared as a Cinch.DataWrapper, to allow the ViewModel
    //to decide what state the data is in, and the View just renders 
    //the data state accordingly
    private Cinch.DataWrapper<Int32> quantity;


    public OrderModel()
    {
        Quantity = new DataWrapper<int32>(this, quantityChangeArgs);
        ....
        ....
        //Setup rules etc etc
        
    }

    static PropertyChangedEventArgs quantityChangeArgs =
        ObservableHelper.CreateArgs<OrderModel>(x => x.Quantity);

    public Cinch.DataWrapper<Int32> Quantity
    {
        get { return quantity; }
        private set
        {
            quantity = value;
            NotifyPropertyChanged(quantityChangeArgs);
        }
    }
}

Notice the setter is private, this is due to the fact that these objects are immutable, and are only allowed to be set in the constructor. The IsEditable and the DataValue can be changed whenever you like though. The other thing to note is that the Model/ViewModel actually use some Reflection on construction to obtain an IEnumerable<DataWrapperBase> which is then used as a cache, so setting any of the cached DataWrapper<T> properties from that point on is very quick. This is achieved as follows:

In the constructor, we have something like this:

C#
using System;

using Cinch;
using MVVM.DataAccess;
using System.ComponentModel;
using System.Collections.Generic;

namespace MVVM.Models
{
    /// <summary>
    /// Respresents a UI Order Model, which has all the
    /// good stuff like Validation/INotifyPropertyChanged/IEditableObject
    /// which are all ready to use within the base class.
    /// 
    /// This class also makes use of <see cref="Cinch.DataWrapper">
    /// Cinch.DataWrapper</see>s. Where the idea is that the ViewModel
    /// is able to control the mode for the data, and as such the View
    /// simply binds to a instance of a <see cref="Cinch.DataWrapper">
    /// Cinch.DataWrapper</see> for both its data and its editable state.
    /// Where the View can disable a control based on the 
    /// <see cref="Cinch.DataWrapper">Cinch.DataWrapper</see> editable state.
    /// </summary>
    public class OrderModel : Cinch.EditableValidatingObject
    {
        #region Data
        //Any data item is declared as a Cinch.DataWrapper, to allow the ViewModel
        //to decide what state the data is in, and the View just renders 
        //the data state accordingly
        private Cinch.DataWrapper<Int32> orderId;
        private Cinch.DataWrapper<Int32> customerId;
        private Cinch.DataWrapper<Int32> productId;
        private Cinch.DataWrapper<Int32> quantity;
        private Cinch.DataWrapper<DateTime> deliveryDate;
        private IEnumerable<DataWrapperBase> cachedListOfDataWrappers;
        #endregion

        #region Ctor
        public OrderModel()
        {
            #region Create DataWrappers

            OrderId = new DataWrapper<Int32>(this, orderIdChangeArgs);
            CustomerId = new DataWrapper<Int32>(this, customerIdChangeArgs);
            ProductId = new DataWrapper<Int32>(this, productIdChangeArgs);
            Quantity = new DataWrapper<Int32>(this, quantityChangeArgs);
            DeliveryDate = new DataWrapper<DateTime>(this, deliveryDateChangeArgs);

            //fetch list of all DataWrappers, so they can be used again later without the
            //need for reflection
            cachedListOfDataWrappers =
                DataWrapperHelper.GetWrapperProperties<OrderModel>(this);

            #endregion
        }
        #endregion
    }
}

And from then on, whenever we deal with the DataWrapper<T> properties, we can use the cached list.

So getting back to how we might use these in our Views, I simply bind to these DataWrapper<T> properties as follows:

XML
<TextBox FontWeight="Normal" FontSize="11" Width="200"
    Cinch:NumericTextBoxBehavior.IsEnabled="True"            
    Text="{Binding Path=CurrentCustomerOrder.Quantity.DataValue,
    UpdateSourceTrigger=LostFocus, ValidatesOnDataErrors=True,
    ValidatesOnExceptions=True}"

    Style="{StaticResource ValidatingTextBox}"
    IsEnabled="{Binding Path=CurrentCustomerOrder.Quantity.IsEditable}"/>

So that's all cool, but how do these DataWrapper<T> objects respond to a change in mode state? Well, that is quite simply actually, we do have a Cinch.ViewMode in the ViewModel and whenever that changes state, we need to update the state of all the nested DataWrapper<T> objects in whatever object is we are trying to change the state for (for me, this is always a UI Model, for others this may be the ViewModel itself).

Here is an example AddEditOrderViewModel, which for me holds a single UI Model of type OrderModel. As I say, others will not like this and would have the ViewModel expose all the properties available within a UI Model of type OrderModel. The thing with MVVM is that you do it your own way, and this is my way. I don't care if InValid data gets to the Model just so long as that Model can not be saved to the database.

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Data;
using System.Linq;

using Cinch;
using MVVM.Models;
using MVVM.DataAccess;

namespace MVVM.ViewModels
{
    /// <summary>
    /// Provides ALL logic for the AddEditOrderView
    /// </summary>
    public class AddEditOrderViewModel : Cinch.WorkspaceViewModel
    {
        private ViewMode currentViewMode = ViewMode.AddMode;
        private OrderModel currentCustomerOrder;

        public AddEditOrderViewModel()
        {

        }

        /// <summary>
        /// The current ViewMode, when changed will loop
        /// through all nested DataWrapper objects and change
        /// their state also
        /// </summary>

        static PropertyChangedEventArgs currentViewModeChangeArgs =
            ObservableHelper.CreateArgs<AddEditOrderViewModel>(x => x.CurrentViewMode);

        public ViewMode CurrentViewMode
        {
            get { return currentViewMode; }
            set
            {
                currentViewMode = value;

                switch (currentViewMode)
                {
                    case ViewMode.AddMode:
                        CurrentCustomerOrder = new OrderModel();
                        this.DisplayName = "Add Order";
                        break;
                    case ViewMode.EditMode:
                        CurrentCustomerOrder.BeginEdit();
                        this.DisplayName = "Edit Order";
                        break;
                    case ViewMode.ViewOnlyMode:
                        this.DisplayName = "View Order";
                        break;
                }

                //Now change all the CurrentCustomer.CachedListOfDataWrappers
                //Which sets all the Cinch.DataWrapper<T>s to the correct IsEditable
                //state based on the new ViewMode applied to the ViewModel
                //we can use the Cinch.DataWrapperHelper class for this
                DataWrapperHelper.SetMode(
                    CurrentCustomer.CachedListOfDataWrappers,
                    currentViewMode);

                NotifyPropertyChanged(currentViewModeChangeArgs);
            }
        }

        /// <summary>
        /// Current Customer OrderModel
        /// </summary>
        static PropertyChangedEventArgs currentCustomerOrderChangeArgs =
            ObservableHelper.CreateArgs<AddEditOrderViewModel>(x => x.CurrentCustomerOrder);

        public OrderModel CurrentCustomerOrder
        {
            get { return currentCustomerOrder; }
            set
            {
                currentCustomerOrder = value;
                if (currentCustomerOrder != null)
                {
                    if (currentCustomerOrder.ProductId.DataValue > 0)
                    {
                        ProductModel prod = this.Products.Where(p => p.ProductId ==
                            currentCustomerOrder.ProductId.DataValue).Single();
                        productsCV.MoveCurrentTo(prod);
                    }
                }
                NotifyPropertyChanged(currentCustomerOrderChangeArgs);
            }
        }

        ....
        ....
        ....
    }
}

One thing worth mentioning here is that when the CurrentViewMode property changes, a DataWrapperHelper class is used to set all the cached DataWrapper<T> objects for a particular object into the same state as that just requested. Here is the code that does that:

C#
// The following functions may be used when dealing with model/viewmodel objects
// whose entire set of DataWrapper properties are immutable (only have a getter
// for the property). They avoid having to do reflection to retrieve the list
// of wrapper properties every time a mode change, edit state change

/// <summary>
/// Set all Cinch.DataWrapper properties to have the correct Cinch.DataWrapper.IsEditable 
/// to the correct state based on the current ViewMode 
/// </summary>
/// <param name="wrapperProperties">The properties on which to change the mode</param>
/// <param name="currentViewMode">The current ViewMode</param>
public static void SetMode(IEnumerable<DataWrapperBase> wrapperProperties,
    ViewMode currentViewMode)
{
    bool isEditable = currentViewMode ==
            ViewMode.EditMode || currentViewMode == ViewMode.AddMode;

    foreach (var wrapperProperty in wrapperProperties)
    {
        try
        {
            wrapperProperty.IsEditable = isEditable;
        }
        catch (Exception)
        {
            Debug.WriteLine("There was a problem setting the currentViewMode");
        }
    }
}

Validation Rules/IDataErrorInfo Integration

I recall some time ago Paul Stovell published a great article Delegates and Business Objects which I simply loved, as it seemed to make so much sense to me. To this end, Cinch makes use of Paul's great idea to use delegates to provide validation for business objects.

The idea is simply, the business object has the AddRule(Rule newRule) method which is used to add rules; the business object also implements IDataErrorInfo, which is the preferred WPF validation technique. Then what basically happens is that when the IDataErrorInfo.IsValid property is called against a particular business object, all the validation rules (delegates) are checked and a list of broken rules (as dictated by the delegate rules added to the object) are presented as the IDataErrorInfo.Error string.

I urge you all to read Paul Stovell's excellent Delegates and Business Objects article first, but basically, Cinch makes use of this.

What Cinch provides is:

  • A ValidatingObject base class that can be used which accepts any Rule based class to be added.
  • SimpleRule, a simple delegate rule
  • RegexRule, a Regular Expression rule
  • Quite nicely only declares the rules once per Type (as they are static fields) which saves on the amount of memory that is required for business object validation

Here is an example of how to use these with Cinch where the property is a simple type such as String/Int32 etc.

C#
public class OrderModel : Cinch.ValidatingObject
{
    private Int32 quantity;

    //rules
    private static SimpleRule quantityRule;

    public OrderModel()
    {
        #region Create Validation Rules
        quantity.AddRule(quantityRule);
        #endregion
    }

    static OrderModel()
    {
        quantityRule = new SimpleRule("Quantity", "Quantity can not be < 0",
                  (Object domainObject)=>
                  {
                      OrderModel obj = (OrderModel)domainObject;
                      return obj.Quantity <= 0;
                  });
    }
}

However, recall I mentioned a special Cinch class to allow the ViewModel to place a single Model field into edit mode using the Cinch.DataWrapper<T>. Well, we need to do something ever so slightly different for those, we need to do the following:

C#
public class OrderModel : Cinch.ValidatingObject
{
    #region Data
    //Any data item is declared as a Cinch.DataWrapper, to allow the ViewModel
    //to decide what state the data is in, and the View just renders 
    //the data state accordingly
    private Cinch.DataWrapper<Int32> customerId;
    
    //rules
    private static SimpleRule quantityRule;    
    #endregion

    #region Ctor
    public OrderModel()
    {
        //setup DataWrappers prior to setting up rules
        ....
        ....

        #region Create Validation Rules

        quantity.AddRule(quantityRule);

        #endregion
    }
    
    static OrderModel()
    {
        quantityRule = new SimpleRule("DataValue", "Quantity can not be < 0",
                  (Object domainObject)=>
                  {
                      DataWrapper<Int32> obj = (DataWrapper<Int32>)domainObject;
                      return obj.DataValue <= 0;
                  });
    }    
    #endregion

    #region Public Properties

    static PropertyChangedEventArgs quantityChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.Quantity);

    public Cinch.DataWrapper<Int32> Quantity
    {
        get { return quantity; }
        private set
        {
            quantity = value;
            NotifyPropertyChanged(quantityChangeArgs);
        }
    }
    
    #endregion
}

We need to declare the validation rule like this for Cinch.DataWrapper<T> objects as they are not simply properties but are actual classes, so we need to specify the DataValue property of the individual Cinch.DataWrapper<T> object to validate for the rule.

This also comes into play within the IsValid method you get when you inherit from a Cinch.ValidatingObject object. Let's say you have something like this for a UI Model object:

C#
using System;

using Cinch;
using MVVM.DataAccess;
using System.ComponentModel;
using System.Collections.Generic;

namespace MVVM.Models
{
    /// <summary>
    /// Respresents a UI Order Model, which has all the
    /// good stuff like Validation/INotifyPropertyChanged/IEditableObject
    /// which are all ready to use within the base class.
    /// 
    /// This class also makes use of <see cref="Cinch.DataWrapper">
    /// Cinch.DataWrapper</see>s. Where the idea is that the ViewModel
    /// is able to control the mode for the data, and as such the View
    /// simply binds to a instance of a <see cref="Cinch.DataWrapper">
    /// Cinch.DataWrapper</see> for both its data and its editable state.
    /// Where the View can disable a control based on the 
    /// <see cref="Cinch.DataWrapper">Cinch.DataWrapper</see> editable state.
    /// </summary>
    public class OrderModel : Cinch.ValidatingObject
    {
        #region Data
        //Any data item is declared as a Cinch.DataWrapper, to allow the ViewModel
        //to decide what state the data is in, and the View just renders 
        //the data state accordingly
        private Cinch.DataWrapper<Int32> orderId;
        private Cinch.DataWrapper<Int32> customerId;
        private Cinch.DataWrapper<Int32> productId;
        private Cinch.DataWrapper<Int32> quantity;
        private Cinch.DataWrapper<DateTime> deliveryDate;
        private IEnumerable<DataWrapperBase> cachedListOfDataWrappers;

        //rules
        private static SimpleRule quantityRule;

        #endregion

        #region Ctor
        public OrderModel()
        {
            #region Create DataWrappers

            OrderId = new DataWrapper<Int32>(this, orderIdChangeArgs);
            CustomerId = new DataWrapper<Int32>(this, customerIdChangeArgs);
            ProductId = new DataWrapper<Int32>(this, productIdChangeArgs);
            Quantity = new DataWrapper<Int32>(this, quantityChangeArgs);
            DeliveryDate = new DataWrapper<DateTime>(this, deliveryDateChangeArgs);

            //fetch list of all DataWrappers, so they can be used again later without the
            //need for reflection
            cachedListOfDataWrappers =
                DataWrapperHelper.GetWrapperProperties<OrderModel>(this);

            #endregion

            #region Create Validation Rules

            quantity.AddRule(quantityRule);

            #endregion


            //I could not be bothered to write a full DateTime picker in
            //WPF, so for the purpose of this demo, DeliveryDate is
            //fixed to DateTime.Now
            DeliveryDate.DataValue = DateTime.Now;
        }

        static OrderModel()
        {
            quantityRule = new SimpleRule("DataValue", "Quantity can not be < 0",
                      (Object domainObject)=>
                      {
                          DataWrapper<Int32> obj = (DataWrapper<Int32>)domainObject;
                          return obj.DataValue <= 0;
                      });
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// OrderId
        /// </summary>
        static PropertyChangedEventArgs orderIdChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.OrderId);

        public Cinch.DataWrapper<Int32> OrderId
        {
            get { return orderId; }
            private set
            {
                orderId = value;
                NotifyPropertyChanged(orderIdChangeArgs);
            }
        }

        /// <summary>
        /// CustomerId
        /// </summary>
        static PropertyChangedEventArgs customerIdChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.CustomerId);

        public Cinch.DataWrapper<Int32> CustomerId
        {
            get { return customerId; }
            private set
            {
                customerId = value;
                NotifyPropertyChanged(customerIdChangeArgs);
            }
        }

        /// <summary>
        /// ProductId
        /// </summary>
        static PropertyChangedEventArgs productIdChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.ProductId);

        public Cinch.DataWrapper<Int32> ProductId
        {
            get { return productId; }
            private set
            {
                productId = value;
                NotifyPropertyChanged(productIdChangeArgs);
            }
        }

        /// <summary>
        /// Quantity
        /// </summary>
        static PropertyChangedEventArgs quantityChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.Quantity);

        public Cinch.DataWrapper<Int32> Quantity
        {
            get { return quantity; }
            private set
            {
                quantity = value;
                NotifyPropertyChanged(quantityChangeArgs);
            }
        }

        /// <summary>
        /// DeliveryDate
        /// </summary>
        static PropertyChangedEventArgs deliveryDateChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.DeliveryDate);

        public Cinch.DataWrapper<DateTime> DeliveryDate
        {
            get { return deliveryDate; }
            private set
            {
                deliveryDate = value;
                NotifyPropertyChanged(deliveryDateChangeArgs);
            }
        }

        /// <summary>
        /// Returns cached collection of DataWrapperBase
        /// </summary>
        public IEnumerable<DataWrapperBase> CachedListOfDataWrappers
        {
            get { return cachedListOfDataWrappers; }
        }
        #endregion

        #region Overrides
        /// <summary>
        /// Is the Model Valid
        /// </summary>
        public override bool IsValid
        {
            get
            {
                //return base.IsValid and use DataWrapperHelper, if you are
                //using DataWrappers
                return base.IsValid &&
                    DataWrapperHelper.AllValid(cachedListOfDataWrappers);

            }
        }
        #endregion
    }
}

You would then need to override the IsValid property to look like this, where we come up with a combined IsValid for the entire object based not only on its IsValid but also the IsValid state of any nested Cinch.DataWrapper<T> object, which is very easy as they also inherit from Cinch.EditableValidatingObject which in turn inherits from Cinch.ValidatingObject, so they already have the IDataErrorInfo implementation, so it's not that hard to cope with.

I know this seems a lot of extra work, but the added benefit of the ViewModel being able to set a Model's individual field editability state, and have the View reflect this seamlessly via bindings, simply can not be ignored.

C#
/// <summary>
/// Override hook which allows us to also put any child 
/// EditableValidatingObject objects IsValid state into
/// a combined IsValid state for the whole Model
/// </summary>
public override bool IsValid
{
    get
    {
       //return base.IsValid and use DataWrapperHelper, if you are
       //using DataWrappers
       return base.IsValid &&
          DataWrapperHelper.AllValid(cachedListOfDataWrappers);
    }
}

Typically, the WPF style we would use for a TextBox that needed to supply validation support for IDataErrorInfo would look something like the following, where we use the Validation.HasError property to change the border color of the TextBox when there is a validation error present.

XML
<Style x:Key="ValidatingTextBox" TargetType="{x:Type TextBoxBase}">
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="OverridesDefaultStyle" Value="True"/>

    <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>

    <Setter Property="MinWidth" Value="120"/>

    <Setter Property="MinHeight" Value="20"/>

    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TextBoxBase}">
                <Border 
                      Name="Border"
                      CornerRadius="5" 
                      Padding="2"

                      Background="White"
                      BorderBrush="Black"
                      BorderThickness="2" >
                    <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>

                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="False">
                        <Setter TargetName="Border" 
                           Property="Background" Value="LightGray"/>

                        <Setter TargetName="Border" 
                           Property="BorderBrush" Value="Black"/>
                        <Setter Property="Foreground" Value="Gray"/>

                    </Trigger>
                    <Trigger Property="Validation.HasError" Value="true">
                        <Setter TargetName="Border" Property="BorderBrush" 
                                Value="Red"/>

                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>

        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip"
                        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                        Path=(Validation.Errors).CurrentItem.ErrorContent}"/>

        </Trigger>

    </Style.Triggers>
</Style>

IEditableObject Support

I have in the past used a pattern called Memento which basically is a cool pattern for support of Undo on business objects. Basically, what it allowed for was the storage of an object's state to a Memento backing object which had the exact same properties as the business object it was storing state for. So when you started an edit on a business object, you store the current state in a Memento and did your edit. If you cancelled the edit, the business object's state would be restored from the Memento. This does work very well, but Microsoft also support this via an interface called IEditableObject which looks like this:

  • BeginEdit()
  • CancelEdit()
  • EndEdit()

So using this interface, we can actually make our business objects store their own state. Now I can take no credit for this next piece of code, it comes from Mark Smith's excellent work. Actually, a fair amount of Cinch is down to Mark Smith's work. Again, I did ask Mark if I could poach his code, he said yes, great cheers Mark.

What Cinch does is provide a base class that can be used to inherit from for your business objects; this base class also supports validation via the IDataErrorInfo interface we just discussed above. Here is how it works. On BeginEdit(), a little bit of Reflection/LINQ is used to store the current object's state in an internal Dictionary. On Canceldit(), the internal Dictionary values are restored to the current object's properties, using the property name as a key into the stored Dictionary state.

This diagram shows this:

Image 2

Here is the Cinch base class that does all this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.Diagnostics;

namespace Cinch
{
    /// <summary>
    /// Provides a IDataErrorInfo validating object that is also
    /// editable by implementing the IEditableObject interface
    /// </summary>
    public abstract partial class EditableValidatingObject :
        ValidatingObject, IEditableObject
    {
        #region Data
        /// <summary>
        /// This stores the current "copy" of the object. 
        /// If it is non-null, then we are in the middle of an 
        /// editable operation.
        /// </summary>
        private Dictionary<string, object> _savedState;
        #endregion

        #region Public/Protected Methods
        /// <summary>
        /// Begins an edit on an object.
        /// </summary>

        public void BeginEdit()
        {
            OnBeginEdit();
            _savedState = GetFieldValues();
        }

        /// <summary>
        /// Interception point for derived logic to do work when beginning edit.
        /// </summary>
        protected virtual void OnBeginEdit()
        {
        }

        /// <summary>
        /// Discards changes since the last 
        /// <see cref="M:System.ComponentModel.IEditableObject.BeginEdit"/> call.
        /// </summary>
        public void CancelEdit()
        {
            OnCancelEdit();
            RestoreFieldValues(_savedState);
            _savedState = null;
        }

        /// <summary>
        /// This is called in response CancelEdit and provides an interception point.
        /// </summary>
        protected virtual void OnCancelEdit()
        {
        }

        /// <summary>
        /// Pushes changes since the last 
        /// <see cref="M:System.ComponentModel.IEditableObject.BeginEdit"/> 
        /// or <see cref="M:System.ComponentModel.IBindingList.AddNew"/> 
        /// call into the underlying object.
        /// </summary>
        public void EndEdit()
        {
            OnEndEdit();
            _savedState = null;
        }

        /// <summary>
        /// This is called in response EndEdit and provides an interception point.
        /// </summary>
        protected virtual void OnEndEdit()
        {
        }

        /// <summary>
        /// This is used to clone the object. 
        /// Override the method to provide a more efficient clone. 
        /// The default implementation simply reflects across 
        /// the object copying every field.
        /// </summary>
        /// <returns>Clone of current object</returns>
        protected virtual Dictionary<string, object> GetFieldValues()
        {
            return GetType().GetProperties(BindingFlags.Public |
                BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(pi => pi.CanRead && pi.GetIndexParameters().Length == 0)
                .Select(pi => new { Key = pi.Name, Value = pi.GetValue(this, null) })
                .ToDictionary(k => k.Key, k => k.Value);

        }

        /// <summary>
        /// This restores the state of the current object from the passed clone object.
        /// </summary>
        /// <param name="fieldValues">Object to restore state from</param>
        protected virtual void RestoreFieldValues(Dictionary<string, object> fieldValues)
        {
            foreach (PropertyInfo pi in GetType().GetProperties(
                BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(pi => pi.CanWrite && pi.GetIndexParameters().Length == 0) )
            {
                object value;
                if (fieldValues.TryGetValue(pi.Name, out value))
                    pi.SetValue(this, value, null);
                else
                {
                    Debug.WriteLine("Failed to restore property " +
                    pi.Name + " from cloned values, property not found in Dictionary.");
                }
            }
        }
        #endregion
    }
}

So all you have to do to get editability support is inherit your UI model objects from Cinch.EditableValidatingObject. Job done.

Let's see how you might put an object that inherits from Cinch.EditableValidatingObject into Edit mode.

Well, from the ViewModel, we can simply do this.CurrentCustomer.BeginEdit(); it's that easy. However, what you must also do is if you have any nested Cinch.DataWrapper<T> objects, make sure they are put into the correct state. You would do this in your UI Model class as follows, where we simply override the protected virtual void OnBeginEdit() we get from inheriting from Cinch.EditableValidatingObject.

Where we may have a UI Model object that looks like:

C#
using System;

using Cinch;
using MVVM.DataAccess;
using System.ComponentModel;
using System.Collections.Generic;

namespace MVVM.Models
{
    /// <summary>
    /// Respresents a UI Order Model, which has all the
    /// good stuff like Validation/INotifyPropertyChanged/IEditableObject
    /// which are all ready to use within the base class.
    /// 
    /// This class also makes use of <see cref="Cinch.DataWrapper">
    /// Cinch.DataWrapper</see>s. Where the idea is that the ViewModel
    /// is able to control the mode for the data, and as such the View
    /// simply binds to a instance of a <see cref="Cinch.DataWrapper">
    /// Cinch.DataWrapper</see> for both its data and its editable state.
    /// Where the View can disable a control based on the 
    /// <see cref="Cinch.DataWrapper">Cinch.DataWrapper</see> editable state.
    /// </summary>
    public class OrderModel : Cinch.EditableValidatingObject
    {
        #region Data
        //Any data item is declared as a Cinch.DataWrapper, to allow the ViewModel
        //to decide what state the data is in, and the View just renders 
        //the data state accordingly
        private Cinch.DataWrapper<Int32> orderId;
        private Cinch.DataWrapper<Int32> customerId;
        private Cinch.DataWrapper<Int32> productId;
        private Cinch.DataWrapper<Int32> quantity;
        private Cinch.DataWrapper<DateTime> deliveryDate;
        private IEnumerable<DataWrapperBase> cachedListOfDataWrappers;

        //rules
        private static SimpleRule quantityRule;

        #endregion

        #region Ctor
        public OrderModel()
        {
            #region Create DataWrappers

            OrderId = new DataWrapper<Int32>(this, orderIdChangeArgs);
            CustomerId = new DataWrapper<Int32>(this, customerIdChangeArgs);
            ProductId = new DataWrapper<Int32>(this, productIdChangeArgs);
            Quantity = new DataWrapper<Int32>(this, quantityChangeArgs);
            DeliveryDate = new DataWrapper<DateTime>(this, deliveryDateChangeArgs);

            //fetch list of all DataWrappers, so they can be used again later without the
            //need for reflection
            cachedListOfDataWrappers =
                DataWrapperHelper.GetWrapperProperties<OrderModel>(this);

            #endregion

            #region Create Validation Rules

            quantity.AddRule(quantityRule);

            #endregion


            //I could not be bothered to write a full DateTime picker in
            //WPF, so for the purpose of this demo, DeliveryDate is
            //fixed to DateTime.Now
            DeliveryDate.DataValue = DateTime.Now;
        }

        static OrderModel()
        {

            quantityRule = new SimpleRule("DataValue", "Quantity can not be < 0",
                      (Object domainObject)=>
                      {
                          DataWrapper<Int32> obj = (DataWrapper<Int32>)domainObject;
                          return obj.DataValue <= 0;
                      });
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// OrderId
        /// </summary>
        static PropertyChangedEventArgs orderIdChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.OrderId);

        public Cinch.DataWrapper<Int32> OrderId
        {
            get { return orderId; }
            private set
            {
                orderId = value;
                NotifyPropertyChanged(orderIdChangeArgs);
            }
        }

        /// <summary>
        /// CustomerId
        /// </summary>
        static PropertyChangedEventArgs customerIdChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.CustomerId);

        public Cinch.DataWrapper<Int32> CustomerId
        {
            get { return customerId; }
            private set
            {
                customerId = value;
                NotifyPropertyChanged(customerIdChangeArgs);
            }
        }

        /// <summary>
        /// ProductId
        /// </summary>
        static PropertyChangedEventArgs productIdChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.ProductId);

        public Cinch.DataWrapper<Int32> ProductId
        {
            get { return productId; }
            private set
            {
                productId = value;
                NotifyPropertyChanged(productIdChangeArgs);
            }
        }

        /// <summary>
        /// Quantity
        /// </summary>
        static PropertyChangedEventArgs quantityChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.Quantity);

        public Cinch.DataWrapper<Int32> Quantity
        {
            get { return quantity; }
            private set
            {
                quantity = value;
                NotifyPropertyChanged(quantityChangeArgs);
            }
        }

        /// <summary>
        /// DeliveryDate
        /// </summary>
        static PropertyChangedEventArgs deliveryDateChangeArgs =
            ObservableHelper.CreateArgs<OrderModel>(x => x.DeliveryDate);

        public Cinch.DataWrapper<DateTime> DeliveryDate
        {
            get { return deliveryDate; }
            private set
            {
                deliveryDate = value;
                NotifyPropertyChanged(deliveryDateChangeArgs);
            }
        }

        /// <summary>
        /// Returns cached collection of DataWrapperBase
        /// </summary>
        public IEnumerable<DataWrapperBase> CachedListOfDataWrappers
        {
            get { return cachedListOfDataWrappers; }
        }
        #endregion

        #region Overrides
        /// <summary>
        /// Is the Model Valid
        /// </summary>
        public override bool IsValid
        {
            get
            {
                //return base.IsValid and use DataWrapperHelper, if you are
                //using DataWrappers
                return base.IsValid &&
                    DataWrapperHelper.AllValid(cachedListOfDataWrappers);

            }
        }
        #endregion

        #region Static Methods
        /// <summary>
        /// Allows Service layer objects to be translated into
        /// UI objects
        /// </summary>
        /// <param name="cust">Service layer object</param>
        /// <returns>UI layer object</returns>
        public static OrderModel OrderToOrderModel(Order order)
        {
            OrderModel orderModel = new OrderModel();
            orderModel.OrderId.DataValue = order.OrderId;
            orderModel.CustomerId.DataValue = order.CustomerId;
            orderModel.Quantity.DataValue = order.Quantity;
            orderModel.ProductId.DataValue = order.ProductId;
            orderModel.DeliveryDate.DataValue = order.DeliveryDate;
            return orderModel;

        }
        #endregion

        #region EditableValidatingObject overrides

        /// <summary>
        /// Override hook which allows us to also put any child 
        /// EditableValidatingObject objects into the BeginEdit state
        /// </summary>
        protected override void OnBeginEdit()
        {
            base.OnBeginEdit();
            //Now walk the list of properties in the OrderModel
            //and call BeginEdit() on all Cinch.DataWrapper<T>s.
            //we can use the Cinch.DataWrapperHelper class for this
            DataWrapperHelper.SetBeginEdit(cachedListOfDataWrappers);
        }

        /// <summary>
        /// Override hook which allows us to also put any child 
        /// EditableValidatingObject objects into the EndEdit state
        /// </summary>
        protected override void OnEndEdit()
        {
            base.OnEndEdit();
            //Now walk the list of properties in the CustomerModel
            //and call CancelEdit() on all Cinch.DataWrapper<T>s.
            //we can use the Cinch.DataWrapperHelper class for this
            DataWrapperHelper.SetEndEdit(cachedListOfDataWrappers);
        }

        /// <summary>
        /// Override hook which allows us to also put any child 
        /// EditableValidatingObject objects into the CancelEdit state
        /// </summary>
        protected override void OnCancelEdit()
        {
            base.OnCancelEdit();
            //Now walk the list of properties in the CustomerModel
            //and call CancelEdit() on all Cinch.DataWrapper<T>s.
            //we can use the Cinch.DataWrapperHelper class for this
            DataWrapperHelper.SetCancelEdit(cachedListOfDataWrappers);

        }
        #endregion
    }
}

What we need to do is override the Cinch.EditableValidatingObject virtual methods as follows:

C#
#region EditableValidatingObject overrides

/// <summary>
/// Override hook which allows us to also put any child 
/// EditableValidatingObject objects into the BeginEdit state
/// </summary>
protected override void OnBeginEdit()
{
    base.OnBeginEdit();
    //Now walk the list of properties in the CustomerModel
    //and call BeginEdit() on all Cinch.DataWrapper<T>s.
    //we can use the Cinch.DataWrapperHelper class for this
    DataWrapperHelper.SetBeginEdit(cachedListOfDataWrappers);
}

/// <summary>
/// Override hook which allows us to also put any child 
/// EditableValidatingObject objects into the EndEdit state
/// </summary>
protected override void OnEndEdit()
{
    base.OnEndEdit();
    //Now walk the list of properties in the CustomerModel
    //and call CancelEdit() on all Cinch.DataWrapper<T>s.
    //we can use the Cinch.DataWrapperHelper class for this
    DataWrapperHelper.SetEndEdit(cachedListOfDataWrappers);
}

/// <summary>
/// Override hook which allows us to also put any child 
/// EditableValidatingObject objects into the CancelEdit state
/// </summary>
protected override void OnCancelEdit()
{
    base.OnCancelEdit();
    //Now walk the list of properties in the CustomerModel
    //and call CancelEdit() on all Cinch.DataWrapper<T>s.
    //we can use the Cinch.DataWrapperHelper class for this
    DataWrapperHelper.SetCancelEdit(cachedListOfDataWrappers);

}
#endregion

Where the Cinch framework provides a static helper called DataWrapperHelper, which you must use to set the correct edit state of the nested DataWrapper<T> objects; you can use these helper methods:

  • DataWrapperHelper.SetBeginEdit(IEnumerable<DataWrapperBase> wrapperProperties)
  • DataWrapperHelper.SetEndEdit(IEnumerable<DataWrapperBase> wrapperProperties)
  • DataWrapperHelper.SetCancelEdit(IEnumerable<DataWrapperBase> wrapperProperties)

Where the IEnumerable<DataWrapperBase> wrapperProperties is actually the cachedListOfDataWrappers which was obtained during the object construction.

You do not have to worry about this, Cinch will do this for you providing you do the right thing in the UI Model class. If you are feeling a little lost by all this, do not worry: one of the subsequent articles will walk through how to create a UI Model using Cinch. This article is more about Cinch internals for those that may be interested.

WeakEvent Creation

Before I start talking about how to create WeakEvents, I think this may be a good place to start a small discussion. I imagine there are loads of readers/.NET developers that think Events in .NET are cool. Well, me too, I love events. The thing is, how many of you think you need to worry much about Garbage Collection, and when dealing with events, .NET manages its own memory via the GC right? Well, yeah it does, but Events are one area that are, shall we say, a little gray in .NET.

Image 3

In the diagram above, there is an object (eventExposer) that declares an event (SpecialEvent). Then, a form is created (myForm) that adds a handler to the event. The form is closed and the expectation is that the form will be released to garbage collection, but it isn't. Unfortunately, the underlying delegate of the event still maintains a strong reference to the form because the form's handler wasn't removed.

The image and text are borrowed from http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx.

In typical applications, it is possible that handlers that are attached to event sources will not be destroyed in coordination with the listener object that attachs the handler to the source. This situation can lead to memory leaks. Windows Presentation Foundation (WPF) introduces a particular design pattern that can be used to address this issue, by providing a dedicated manager class for particular events and implementing an interface on listeners for that event. This design pattern is known as the WeakEvent pattern.

MSDN: http://msdn.microsoft.com/en-us/library/aa970850.aspx

Now if you have ever looked into the Weak Event manager / interface implementation, you will realise it is quite a lot of work and you must have a new WeakEventManager per Event type. This to me sounds like too much work, so I would prefer some other mechanisms, such as have a WeakEvent in the beginning. Better still, maybe have a weak listener that only reacts to the source event if the source of the event is still alive and has not been GC'd.

Raise a WeakEvent<T>

So without further ado, let me show you some handy little helpers that are available within Cinch when dealing with Events, and possibly making them Weak. It is still obviously better to Add/Remove the delegates for an event manually where possible, but sometimes you just do not know the lifecycles of the objects involved, so it is preferable to opt for a WeakEvent strategy.

Firstly, let's use the absolutely brilliant WeakEvent<T> from the very, very talented Daniel Grunwald, who published a superb article on WeakEvents some time ago. Daniel's WeakEvent<T> shows how you can raise an event in a weak manner.

I am not going to bore you with all the code for WeakEvent<T>, but one thing that you should probably get familiar with, if you are not already, is the WeakReference class. This is a standard .NET class, which references an object while still allowing that object to be reclaimed by garbage collection.

Pretty much any WeakEvent subscription/raising of event will use an internal WeakReference class to allow the source of the event or the subscriber to be GC'd.

Anyway, to use Daniel Grunwald's WeakEvent<T>, we can do the following:

Declaring the WeakEvent<T>

C#
private readonly WeakEvent<EventHandler<EventArgs>> 
     dependencyChangedEvent =
         new WeakEvent<EventHandler<EventArgs>>();

public event EventHandler<EventArgs> DependencyChanged
{
  add { dependencyChangedEvent.Add(value); }
  remove { dependencyChangedEvent.Remove(value); }
}

Raising the WeakEvent<T>

C#
dependencyChangedEvent.Raise(this, new EventArgs());

Listening to WeakEvent<T>

C#
SourceDependency.DependencyChanged += OnSourceChanged;
...
private void OnSourceChanged(object sender, EventArgs e)
{

}

So that is how you could make a WeakEvent<T>, but sometimes it is not your own code and you are not in charge of the Events contained in the code. Perhaps you are using a third party control set. So in that case, you may need to use a WeakEvent subscription. Cinch provides two methods of doing this.

WeakEvent Subscription

Above we saw how to raise a WeakEvent using Daniel Grunwald's WeakEvent<T>, so how about in the case where we want to subscribe to an existing event? Again, this is typically achieved using a WeakReference class to check the WeakReference.Target for null. If the value is null, the source of the event has been garbage collected so do not fire the invocation list delegate. If it is not null, the source of the event is alive so call the invocation list delegate which subscribed to the event.

Cinch provides two methods to do this.

WeakEventProxy

Which is a neat little class which Paul Stovell wrote some time ago. The entire class looks like this:

C#
using System;

namespace Cinch
{
    public class WeakEventProxy<TEventArgs> : IDisposable
        where TEventArgs : EventArgs
    {
        #region Data
        private WeakReference callbackReference;
        private readonly object syncRoot = new object();
        #endregion

        #region Ctor
        /// <summary>
        /// Initializes a new instance of the <see 
          /// cref="WeakEventProxy<TEventArgs>"/> class.
        /// </summary>

        /// <param name="callback">The callback.</param>
        public WeakEventProxy(EventHandler<TEventArgs> callback)
        {
            callbackReference = new WeakReference(callback, true);
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Used as the event handler which should be subscribed to source collections.
        /// </summary>

        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void Handler(object sender, TEventArgs e)
        {
            //acquire callback, if any
            EventHandler<TEventArgs> callback;
            lock (syncRoot)
            {
                callback = callbackReference == null ? null : 
            callbackReference.Target as EventHandler<TEventArgs>;
            }

            if (callback != null)
            {
                callback(sender, e);
            }
        }

        /// <summary>

        /// Performs application-defined tasks associated with freeing, 
          /// releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            lock (syncRoot)
            {
                GC.SuppressFinalize(this);

                if (callbackReference != null)
                {
                    //test for null in case the reference was already cleared
                    callbackReference.Target = null;
                }

                callbackReference = null;
            }
        }
        #endregion
    }
}

And to use this, we can simply do the following:

Declare event handlers like:

C#
private EventHandler<NotifyCollectionChangedEventArgs> 
     collectionChangeHandler;
private WeakEventProxy<NotifyCollectionChangedEventArgs> 
     weakCollectionChangeListener;

And wire up the event subscription delegate like:

C#
if (weakCollectionChangeListener == null)
{
  collectionChangeHandler = OnCollectionChanged;
  weakCollectionChangeListener = 
     new WeakEventProxy<NotifyCollectionChangedEventArgs>(
         collectionChangeHandler);
}
ncc.CollectionChanged += weakCollectionChangeListener.Handler;


private void OnCollectionChanged(object sender, 
 NotifyCollectionChangedEventArgs e)
{

}

WeakEvent Subscriber With Auto Un-subscription

I was trawling the internet one day and found this superb article on WeakEvents: http://diditwith.net/PermaLink,guid,aacdb8ae-7baa-4423-a953-c18c1c7940ab.aspx. This links contained some cool code that I have used within Cinch, which not only allows users to create WeakEvent subscriptions, but allows the user to specify an auto unsubscribe callback delegate. In addition, using a small variation to this code, it is possible to make all subscribed event handlers weak. Let's have a quick look at the syntax for both these operations:

Specifying A WeakEvent Subscription With Unhook

We simply do this:

C#
workspace.CloseWorkSpace +=
    new EventHandler<EventArgs>(OnCloseWorkSpace).
       MakeWeak(eh => workspace.CloseWorkSpace -= eh);
       
private void OnCloseWorkSpace(object sender, EventArgs e)
{
}

That one line created a Weak listener with an auto unsubscribe. Neat, huh?

I mentioned that you can also use this code to create a WeakEvent, such that all subscribers to a particular event would be Weak. This is how you could do that using this code:

C#
public class EventProvider
{
    private EventHandler<EventArgs> closeWorkSpace;
    public event EventHandler<EventArgs> CloseWorkSpace
    {
        add
        {
            closeWorkSpace += value.MakeWeak(eh => closeWorkSpace -= eh);
        }
        remove
        {
        }
    }
}

As I say, I can not take much credit for this code, it came from the link specified, but I do think it's very handy. We actually use it in production code without too many issues. The only thing I have noticed is that it doesn't play well with the CollectionChanged of ObservableCollection<T>, but then I just use the WeakEventProxy that I also mentioned above that is part of Cinch, and that works just fine.

Mediator Messaging

Now, I do not know about you, but generally, when I work with the MVVM framework, I do not have a single ViewModel managing the whole shooting match. I actually have a number of them (in fact, we have loads). One thing that is an issue using the standard MVVM pattern is cross ViewModel communication. After all, the ViewModels that form an application may all be disparate unconnected objects that know nothing about each other. However, they need to know about certain actions that a user performed. Here is a concrete example.

Say you have two Views one with customers and one with orders for a customer. Let's say the orders view was using a OrdersViewModel and that the customers view was using a CustomersViewModel, and when a customer's order is updated, deleted, or added to that, the Customer view should show some sort of visual trigger to alert the user that some order detail of the customer changed.

Sounds simple enough, right? However, we have two independent views run by two independent ViewModels, with no link, but clearly, there needs to be some sort of connection from the OrdersViewModel to the CustomersViewModel, some sort of messaging perhaps.

This is exactly what the Mediator pattern is all about, it is a simple light weight messaging system. I wrote about this some time ago on my blog, which in turn got made a ton better by Josh Smith / Marlon Grech (as an atomic pair) who came up with the Mediator implementation you will see in Cinch.

So how does the Mediator work?

This diagram may help:

Image 4

The idea is a simple one, the Mediator listens for incoming messages, sees who is interested in a particular message, and calls each of those that are subscribed against a given message. The messages are usually strings.

Basically, what happens is that there is a single instance of the Mediator sitting somewhere (usually exposed as a static property on the ViewModelBase class) that is waiting for objects to subscribe to it either using:

  • An entire object reference. Then any Mediator message methods that have been marked up with the MediatorMessageSinkAttribute attribute will be located on the registered object (using Reflection) and will have a callback delegate automatically created.
  • An actual Lambda callback delegate.

In either case, the Mediator maintains a list of WeakAction's callback delegates. Where each WeakAction is a delegate which uses an internal WeakReference class to check the WeakReference.Target for null, before calling back the delegate. This caters for the fact that the target of the callback delegate may no longer be alive as it may have been Garbage Collected. Any instance of the WeakAction's callback delegates that point to objects that are no longer alive are removed from the list of the Mediator WeakAction's callback delegates.

When a callback delegate is obtained, either the original callback delegate is called or the Mediator message methods that have been marked up with the MediatorMessageSinkAttribute attribute will be called.

Here is an example of how to use the Mediator in all the different possible manners.

Registering for Messages

Using an explicit callback delegate (this is not my preferred option though)

We simply create the correct type of delegate and register a callback for a message notification with the Mediator.

C#
public delegate void DummyDelegate(Boolean dummy);
...

Mediator.Register("AddCustomerMessage", new DummyDelegate((x) =>
{
    AddCustomerCommand.Execute(null);
}));

Register an entire object, and use the MediatorMessageSinkAttribute attribute

This is my favourite approach and is the simplest approach in my opinion. You just need to register an entire object with the Mediator and attribute up some message hook methods.

So to register, this is done for you in Cinch, so you don't have to do anything. Simply inherit from ViewModelBase, job done. If you are wondering how this is done, the ViewModelBase class in Cinch simply registers itself with the Mediator like this:

C#
//Register all decorated methods to the Mediator
Mediator.Register(this);

So any method that is marked up with the MediatorMessageSinkAttribute attribute will be located on the registered object (using Reflection) and will have a callback delegate automatically created. Here is an example:

C#
/// <summary>
/// Mediator callback from StartPageViewModel
/// </summary>
/// <param name="dummy">Dummy not needed</param>

[MediatorMessageSink("AddCustomerMessage"))]
private void AddCustomerMessageSink(Boolean dummy)
{
    AddCustomerCommand.Execute(null);
}

So how about notification of messages?

Message Notification

That is very easy to do, we simply use the Mediator.NotifyCollegues() method as follows:

C#
//Use the Mediator to send a Message to MainWindowViewModel to add a new 
//Workspace item
Mediator.NotifyColleagues<Boolean>("AddCustomerMessage", true);

You can also use the Mediator asynchronously as follows:

C#
Mediator.NotifyColleaguesAsync<Boolean>("AddCustomerMessage", true);

At present, the Cinch ViewModelBase class registers the ViewModel instance with the Mediator, within the ViewModel constructor, and unregisters the ViewModel instance with the Mediator within the Dispose() method.

So any object that subscribes to this message will now be called back by the list of the Mediator WeakAction's callback delegates.

What's Coming Up?

In the subsequent articles, I will be showcasing it roughly like this:

  1. A walkthrough of Cinch, and its internals: II
  2. How to develop ViewModels using Cinch
  3. How to Unit test ViewModels using a Cinch app, including how to test background worker threads which may run within Cinch ViewModels
  4. A demo app using Cinch

That's It, Hope You Liked It

That is actually all I wanted to say right now, but I hope from this article you can see where Cinch is going and how it could help you with MVVM. As we continue our journey, we will be covering the remaining items within Cinch, and then we will move on to see how to develop an app with Cinch.

Thanks

As always, votes / comments are welcome.

History

  • 19/07/2009: Initial release
  • 25/07/2009:
    • Altered NumericTextBoxBehavior to take into account pasted data
    • Changed INotifyPropertyChanged to use static LINQ Expression Trees
  • 02/08/2009: Changed INotifyPropertyChanged to use Phil's (a reader's) idea, which you can read more about here: CinchII.aspx?msg=3141144#xx3141144xx
  • 15/08/2009: Added support for DataWrappers to notify parent when an internal DataWrapper property changes
  • 12/09/2009: Adding caching support to DataWrapper and fixed RegExRule
  • 05/12/09: Added new code sections to show users how to add validation rules using the new validation methods within Cinch
  • 24/12/09: Updated code snippet for NumericTextBoxBehavior
  • 07/05/09: Updated article to show new ViewModelBase view lifecycle properties and updated DataWrapper code, and also Mediator usage was updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
BugBug (and suggested fix) in NumericTextBoxBehaviour RegEx format Pin
AMI28914-May-19 21:47
AMI28914-May-19 21:47 
QuestionMediator Example not working Pin
Member 823590010-Mar-13 5:14
Member 823590010-Mar-13 5:14 
QuestionModels to support ORM mapping Pin
Siddhartha S.7-Oct-12 8:51
Siddhartha S.7-Oct-12 8:51 
AnswerRe: Models to support ORM mapping Pin
Siddhartha S.7-Oct-12 9:05
Siddhartha S.7-Oct-12 9:05 
GeneralRe: Models to support ORM mapping Pin
Sacha Barber8-Oct-12 3:30
Sacha Barber8-Oct-12 3:30 
GeneralRe: Models to support ORM mapping Pin
Siddhartha S.8-Oct-12 3:33
Siddhartha S.8-Oct-12 3:33 
QuestionCinch releases Pin
mwpowellhtx10-Jun-12 5:32
mwpowellhtx10-Jun-12 5:32 
AnswerRe: Cinch releases Pin
Sacha Barber10-Jun-12 7:09
Sacha Barber10-Jun-12 7:09 
GeneralMediator Pattern breaks down when having 2 same windows opened Pin
Fitim Skenderi24-Aug-10 5:02
professionalFitim Skenderi24-Aug-10 5:02 
GeneralRe: Mediator Pattern breaks down when having 2 same windows opened Pin
Sacha Barber24-Aug-10 5:11
Sacha Barber24-Aug-10 5:11 
GeneralRe: Mediator Pattern breaks down when having 2 same windows opened Pin
Fitim Skenderi24-Aug-10 22:51
professionalFitim Skenderi24-Aug-10 22:51 
GeneralRe: Mediator Pattern breaks down when having 2 same windows opened Pin
Sacha Barber24-Aug-10 23:25
Sacha Barber24-Aug-10 23:25 
GeneralRe: Mediator Pattern breaks down when having 2 same windows opened Pin
Fitim Skenderi25-Aug-10 1:37
professionalFitim Skenderi25-Aug-10 1:37 
GeneralRe: Mediator Pattern breaks down when having 2 same windows opened Pin
Sacha Barber25-Aug-10 3:10
Sacha Barber25-Aug-10 3:10 
QuestionStateFull View Pin
sherif_samir31-Jul-10 15:31
sherif_samir31-Jul-10 15:31 
AnswerRe: StateFull View Pin
Sacha Barber31-Jul-10 21:04
Sacha Barber31-Jul-10 21:04 
GeneralNumeric textbox as a behavior Pin
Verbiest Kristof27-Jul-10 2:19
Verbiest Kristof27-Jul-10 2:19 
GeneralRe: Numeric textbox as a behavior Pin
Sacha Barber27-Jul-10 2:36
Sacha Barber27-Jul-10 2:36 
GeneralEventCommander Error - Cannot get write-only property '....Commanding.EventCommander.Mappings' in .NET 4 Pin
bgort23-Apr-10 4:37
bgort23-Apr-10 4:37 
GeneralRe: EventCommander Error - Cannot get write-only property '....Commanding.EventCommander.Mappings' in .NET 4 Pin
Sacha Barber23-Apr-10 5:14
Sacha Barber23-Apr-10 5:14 
GeneralRe: EventCommander Error - Cannot get write-only property '....Commanding.EventCommander.Mappings' in .NET 4 Pin
dmikon7-May-10 12:26
dmikon7-May-10 12:26 
GeneralRe: EventCommander Error - Cannot get write-only property '....Commanding.EventCommander.Mappings' in .NET 4 Pin
Sacha Barber8-May-10 19:58
Sacha Barber8-May-10 19:58 
GeneralAttached Command Behaviour breaks VS2008 xaml editor Pin
carlucci1-Apr-10 5:22
carlucci1-Apr-10 5:22 
GeneralRe: Attached Command Behaviour breaks VS2008 xaml editor Pin
Sacha Barber1-Apr-10 5:29
Sacha Barber1-Apr-10 5:29 
GeneralRe: Attached Command Behaviour breaks VS2008 xaml editor Pin
Sacha Barber1-Apr-10 5:40
Sacha Barber1-Apr-10 5:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.