Posted by: Zeeshan Amjad | March 29, 2013

Switching to MVVM Part 2


We saw one example to see how can we move the existing project using MVVM here. But those are just the first steps in that direction not a complete solution. In any big project we are not using MVVM alone, but use it with other high level patterns. The most commonly used patterns are Command pattern, Mediator pattern and Event to Command pattern.

WPFPattern

It is better to have some reusable classes to implement some functionality. We have one implementation of ICommand interface here.

Code Snippet
using System;
using System.Windows.Input;

namespace WpfPattern
{
    public sealed class MyCommand<T1, T2> : ICommand
    {
        private Action _FunctionWithParam;
        private Action<T1> _Function;
        private Func<T2, bool> _Predicate;

        public MyCommand(Action functionWithoutParam)
        {
            _FunctionWithParam = functionWithoutParam;
        }

        public MyCommand(Action<T1> function)
        {
            _Function = function;
        }

        public MyCommand(Action functionWithoutParam, Func<T2, bool> predicate)
        {
            _FunctionWithParam = functionWithoutParam;
            _Predicate = predicate;
        }

        public MyCommand(Action<T1> function, Func<T2, bool> predicate)
        {
            _Function = function;
            _Predicate = predicate;
        }

        public void Execute(T1 parameter)
        {
            if (_Function != null)
            {
                _Function(parameter);
            }
        }

        public bool CanExecute(T2 parameter)
        {
            if (_Predicate != null)
            {
                return _Predicate(parameter);
            }

            return true;
        }

        #region Implement ICommand interface

        public bool CanExecute(object parameter)
        {
            return CanExecute((T2)parameter);
        }

        public void Execute(object parameter)
        {
            if (_FunctionWithParam != null)
            {
                _FunctionWithParam();
            }
            else
            {
                Execute((T1)parameter);
            }
        }

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

        #endregion
    }
}

Event to command pattern is define here. Here are two classes to implement this functionality.

Code Snippet
using System.Windows;
using System.Windows.Input;

namespace WpfPattern
{
    public sealed class EventBehavior
    {
        #region Fields

        private DependencyProperty property;
        private RoutedEvent routedEvent;

        #endregion

        #region Constructor

        public EventBehavior(RoutedEvent routedEvent)
        {
            this.routedEvent = routedEvent;
        }

        #endregion

        public void CallBack(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            UIElement element = obj as UIElement;

            property = args.Property;

            if (element != null)
            {
                if (args.OldValue != null)
                {
                    element.AddHandler(routedEvent, new RoutedEventHandler(EventHandler));
                }

                if (args.NewValue != null)
                {
                    element.AddHandler(routedEvent, new RoutedEventHandler(EventHandler));
                }
            }
        }

        private void EventHandler(object sender, RoutedEventArgs e)
        {
            DependencyObject obj = sender as DependencyObject;

            if (obj != null)
            {
                ICommand command = obj.GetValue(property) as ICommand;

                if (command != null)
                {
                    if (command.CanExecute(e))
                    {
                        command.Execute(e);
                    }
                }
            }
        }
    }
}

 

And here is one template factory class.

Code Snippet
using System.Windows;
using System.Windows.Input;

namespace WpfPattern
{
    public sealed class BehaviorFactory<T>
    {
        #region Private Constructor

        private BehaviorFactory()
        {

        }

        #endregion

        public static DependencyProperty CreateBehavior(string name, RoutedEvent routedEvent)
        {
            EventBehavior eb = new EventBehavior(routedEvent);

            DependencyProperty dp = DependencyProperty.RegisterAttached(name, typeof(ICommand), typeof(T),
                new PropertyMetadata(eb.CallBack));

            return dp;
        }

    }
}

 

Here is a mediator class defined here.

Code Snippet
using System;

namespace WpfPattern
{
    public class WeakReferenceWrapper
    {
        private WeakReference weakReference;

        public WeakReferenceWrapper(object receiever, Delegate action)
        {
            weakReference = new WeakReference(receiever);
            this.Action = action;
        }

        public Delegate Action
        { get; set; }
    }
}

 

And here is Mediator class.

Code Snippet
using System;
using System.Collections.Generic;

namespace WpfPattern
{
    public sealed class Mediator
    {
        private Dictionary<string, List<WeakReferenceWrapper>> actions =
             new Dictionary<string, List<WeakReferenceWrapper>>();

        public Mediator()
         {
         }

        public void Register<T>(string message, object receiever, Action<T> action)
         {
             if (message == null)
             {
                 throw new ArgumentNullException("message");
             }

            if (action == null)
             {
                 throw new ArgumentNullException("action");
             }

            lock (actions)
             {
                 if (!actions.ContainsKey(message))
                 {
                     actions[message] = new List<WeakReferenceWrapper>();
                 }

                actions[message].Add(new WeakReferenceWrapper(receiever, action));
             }
         }

        public void UnRegister<T>(string message, object receiever, Action<T> action)
         {
             if (message == null)
             {
                 throw new ArgumentNullException("message");
             }

            if (action == null)
             {
                 throw new ArgumentNullException("action");
             }

            lock (actions)
             {
                 if (actions.ContainsKey(message))
                 {
                     List<WeakReferenceWrapper> actionlist = actions[message];

                    actionlist.Remove(new WeakReferenceWrapper(receiever, action));

                    if (actionlist.Count == 0)
                     {
                         actions.Remove(message);
                     }
                 }
             }
         }

        public void Send<T>(string message, T param)
         {
             if (message == null)
             {
                 throw new ArgumentNullException("message");
             }

            lock (actions)
             {
                 if (actions.ContainsKey(message))
                 {
                     List<WeakReferenceWrapper> actionslist = actions[message];

                    foreach (var action in actionslist)
                     {
                         action.Action.DynamicInvoke(param);
                     }
                 }
             }
         }
    }
}

 

Similarly we defined the ViewModel based class here to reuse some common functionality. Here is our ViewModelBase class.

Code Snippet
using System;
using System.ComponentModel;
using System.Windows.Input;

namespace WpfPattern
{
    public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
    {
        private bool _disposed;
        public event EventHandler RequestClose;

        public ICommand ExitCommand
        { get; set; }

        #region Constructor

        public ViewModelBase()
        {
            _disposed = false;
        }

        #endregion

        public void Close()
        {
            EventHandler handler = this.RequestClose;

            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        #region NotifyPropertyChanged Methods

        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Disposable Methods

        public void Dispose()
        {
            Dispose(true);

            // Use SupressFinalize in case a subclass
            // of this type implements a finalizer.
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    // Dispose Managed Resource
                }

                // Dispose Unmanaged Resources

                _disposed = true;
            }
        }

        #endregion
    }
}

 

As we noticed from the above examples that these high level patterns internally uses further primitive patterns. For example, MVVM uses Command, Disposable and Notification pattern, Event to command uses Factory pattern and Mediator uses factory pattern. Here is a diagram to represent this relationship.

WPFPattern2

Here we assumed that we are creating a simple MVVM based WPF application. In more advanced scenarios which as when one view model may contain other view model, we may use composite pattern, in case of complex interaction between some utility classes, we may uses façade and in case of handling different interface we may select adaptor pattern.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: