Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF: If Carlsberg did MVVM Frameworks: Part 3 of n

0.00/5 (No votes)
7 Aug 2010 4  
It would probably be like Cinch, an MVVM framework for WPF.

Contents

Cinch Article Series Links

Introduction

Last time we started looking at some of the Cinch internals, and this time we are going to finish up looking at the Cinch internals.

In this article, we will be looking at the following:

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

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.

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 the 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 II

This section will finish the dive into the internals of Cinch, which should hopefully not bore you lot too much, and should allow you to fully understand the rest of the articles which deal with building a demo set of ViewModels/Unit Tests and showcase the actual attached demo app.

DI/IOC

The Cinch MVVM framework makes use of an IOC container. By default, this is the Microsoft Unity IOC container, which is freely available as a standalone application block or as part of the Enterprise Library. Cinch allows you to choose your own IOC Container and pass that into the ViewModel constructors; all you have to do is implement the IIOCProvider interface, which looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Cinch
{
    /// <summary>
    /// This is an interface that allows different IOC Container providers.
    /// Providers can implement this interface in order to provide the services
    /// from the container.
    /// 
    /// Cinch uses a UnityProvider as default, but you can override this
    /// by supplying a new IIOCProvider variant to the constructor
    /// of your custom Cinch based ViewModels. If no constructor parameter
    /// is supplied to the ViewModelBase class then the default UnityProvider
    /// will be used by Cinch
    /// </summary>
    public interface IIOCProvider
    {
        /// <summary>
        /// Method that <see cref="ViewModelBase">ViewModelBase</see>
        /// can call to tell container to set its self up. You are expected
        /// to register the following Types in this method
        /// <c>ILogger</c>
        /// <c>IUIVisualizerService</c>
        /// <c>IMessageBoxService</c>
        /// <c>IOpenFileService</c>
        /// <c>ISaveFileService</c>
        /// </summary>
        void SetupContainer();

        /// <summary>
        /// Get service from container
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <returns>The service instance</returns>
        T GetTypeFromContainer<T>();
    }
}

Cinch uses the Unity IOC container (as default, but as just stated, you can swap that out by creating your own IOC Container by implementing the IIOCProvider interface) to allow different service implementations to be dynamically injected at runtime.

Here is what the default UnityProvider looks like:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Configuration;

namespace Cinch
{
    /// <summary>
    /// Provides a Unity IOC Container resolver
    /// which Cinch uses as default, which registers defaults
    /// for the following types, but will also allow these defaults
    /// to be overriden if a Unity config section scecifies different
    /// implementations
    /// <c>ILogger</c>
    /// <c>IUIVisualizerService</c>
    /// <c>IMessageBoxService</c>
    /// <c>IOpenFileService</c>
    /// <c>ISaveFileService</c>
    /// </summary>
    public class UnityProvider : IIOCProvider
    {
        #region Ctor
        public UnityProvider()
        {

        }
        #endregion

        #region Private Methods
        /// <summary>
        /// This method registers default services with the service provider. 
        /// These can be overriden by providing a new service implementation 
        /// and a new Unity config section in the project where the new service 
        /// implementation is defined 
        /// </summary>
        private static void RegisterDefaultServices()
        {

            //try add other default services, users can override this 
            //using specific Unity App.Config section entry
            try
            {

                //ILogger : Register a default WPFSLFLogger
                UnitySingleton.Instance.Container.RegisterInstance(
                    typeof(ILogger), new WPFSLFLogger());

                //IUIVisualizerService : Register a default WPFUIVisualizerService
                UnitySingleton.Instance.Container.RegisterInstance(
                    typeof(IUIVisualizerService), new WPFUIVisualizerService());

                //IMessageBoxService : Register a default WPFMessageBoxService
                UnitySingleton.Instance.Container.RegisterInstance(
                    typeof(IMessageBoxService), new WPFMessageBoxService());

                //IOpenFileService : Register a default WPFOpenFileService
                UnitySingleton.Instance.Container.RegisterInstance(
                    typeof(IOpenFileService), new WPFOpenFileService());

                //ISaveFileService : Register a default WPFSaveFileService
                UnitySingleton.Instance.Container.RegisterInstance(
                    typeof(ISaveFileService), new WPFSaveFileService());

            }
            catch (ResolutionFailedException rex)
            {
                String err = String.Format(
                    "An exception has occurred in " + 
                    "unityProvider.RegisterDefaultServices()\r\n{0}",
                    rex.StackTrace.ToString());
#if debug
                Debug.WriteLine(err);
#endif
                Console.WriteLine(err);
                throw rex;
            }
            catch (Exception ex)
            {
                String err = String.Format(
                    "An exception has occurred in " + 
                    "unityProvider.RegisterDefaultServices()\r\n{0}",
                    ex.StackTrace.ToString());
#if debug
                Debug.WriteLine(err);
#endif
                Console.WriteLine(err);
                throw ex;
            }
        }
        #endregion

        #region IIOCProvider Members
        /// <summary>
        /// Register defaults and sets up Unity container
        /// Method that <see cref="ViewModelBase">ViewModelBase</see>
        /// can call to tell container to set its self up
        /// </summary>
        public void SetupContainer()
        {
            try
            {
                //regiser defaults
                RegisterDefaultServices();

                //configure Unity (there could be some different Service implementations
                //in the config that override the defaults just setup
                UnityConfigurationSection section = (UnityConfigurationSection)
                               ConfigurationManager.GetSection("unity");
                if (section != null && section.Containers.Count > 0)
                {
                    section.Containers.Default.Configure(UnitySingleton.Instance.Container);
                }

            }
            catch (Exception ex)
            {
                throw new ApplicationException(
                  "There was a problem configuring the Unity container\r\n" + ex.Message);
            }
        }


        /// <summary>
        /// Get service from container
        /// </summary>
        /// <typeparam name="T">Service type</typeparam>
        /// <returns>The service instance</returns>
        public T GetTypeFromContainer<T>()
        {
            return (T)UnitySingleton.Instance.Container.Resolve(typeof(T));
        }
        #endregion
    }
}

There are some defaults assumed, but these can be overridden by specifying an entry in the App.Config, which will override the default service implementation that might otherwise have been used.

As Cinch is aimed at being a WPF framework, the defaults for most services are WPF implementations. As such, when you come to do a Unit Test project, you must supply test service implementations (Cinch has these available) via the App.Config of the Unit Test project.

This is achieved using the custom UnityConfigurationSection which is filled in, in both the real UI project and also a Unit Test project. The Unity container simply examines the active project's App.Config and reads the Types from the UnityConfigurationSection, and will then create and hold an instance of the configuration specified type.

Cinch wraps the unity container in a singleton, to ensure that there is only ever one Unity container available within a Cinch application.

The following diagram illustrates how the Unity IOC container works:

Optional App.Config Unity Items Required

The following table illustrates what you could supply in the App.Config when using Cinch.

Service Item WPF App Test Project
ILogger Not required, default is used You can use the Cinch WPF service version.
IMessageBoxService Not required, default is used You can use the Cinch test service version default, but you must provide an entry in the Unity config section to ensure the default WPF implementation is overridden in Cinch to use the Test version.
IOpenFileService Not required, default is used You can use the Cinch test service version default, but you must provide an entry in the Unity config section to ensure the default WPF implementation is overridden in Cinch to use the Test version.
ISaveFileService Not required, default is used You can use the Cinch test service version default, but you must provide an entry in the Unity config section to ensure the default WPF implementation is overridden in Cinch to use the Test version.
IUIVisualizerService Not required, default is used, but you should provide the popups this service manages in the constructor of the WPF app's main window, or some other suitable place You can use the Cinch test service version default, but you must provide an entry in the Unity config section to ensure the default WPF implementation is overridden in Cinch to use the Test version.

As shown above, if you are planning on using all the default Cinch services, you do not need to provide an App.Config for the main WPF app, though you must for any test project to ensure the default services (the WPF implementations) are overridden inside the Cinch service resolution code.

Actual Application

Any application based on Cinch does not really need to provide any service implementations as default WPF ones are added and used internally. If however you wish to change one of the default WPF services, you must supply a new Unity container App.Config section which overrides the default implementation of the service type.

Here is an example App.Config for a Cinch app. Remember this must include any services that you may have changed. Shown below is a specialization of the Cinch.IUIVisualizerService service that may provide extra functionality.

You must also provide configuration for the logging within Cinch, which now makes use of a Logging Facade called Simple Logging Facade (SLF) which caters for many different loggers. For Cinch, I chose to use log4Net, so the App.Config for a Cinch based application should look quite similar to that shown below:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>

    <section name="unity"
        type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
              Microsoft.Practices.Unity.Configuration" />  

    <section name="log4net" 
             type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
    <section name="slf" 
             type="Slf.Config.SlfConfigurationSection, slf"/>
  </configSections>

 <!-- Unity Config Section -->
  <unity>
    <containers>
      <container>
 
        <types>
 
          <type
              type="Cinch.IUIVisualizerService, Cinch"
              mapTo="MVVM.Demo.MyFunkyWPFUIVisualizerService, MVVM.Demo"/>
 
        </types>
      </container>
    </containers>
 
  </unity>

  <slf>
    <factories>
      <!-- configure single log4net factory, 
           which will get all logging output -->
      <!-- Important: Set a reference to the log4net facade 
           library to make sure it will be available at runtime -->
      <factory type="SLF.Log4netFacade.Log4netLoggerFactory, 
               SLF.Log4netFacade"/>
    </factories>
  </slf>


  <!-- configures log4net to write into a local file called "log.txt" -->
  <log4net>
    <!--  log4net uses the concept of 'appenders' to indicate where 
          log messages are written to.
          Appenders can be files, the console, databases, SMTP and much more
    -->
    <appender name="MainAppender" type="log4net.Appender.FileAppender">
      <param name="File" value="log.txt" />
      <param name="AppendToFile" value="true" />
      <!--  log4net can optionally format the logged messages with a pattern. 
            This pattern string details what information
            is logged and the format it takes. 
            A wide range of information can be logged, including message, 
            thread, identity and more,
            see the log4net documentation for details:
            http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
      -->
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%logger: %date [%thread] 
                           %-5level - %message %newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="MainAppender" />
    </root>
  </log4net>

</configuration>

You can see that the app's implementation of the Cinch.IUIVisualizerService service is injected using Unity into Cinch, and will override the default implementation of Cinch.IUIVisualizerService within Cinch, where Cinch would have previously tried to have used the default WPF implementation.

Unit Tests

Cinch is designed to be Unit testable, and as such, you can supply alternative services to Cinch using Injection/Unity. But to be honest, it is more than likely the default implementations supplied with Cinch will be more than adequate, but you can decide that after you have read about them below.

Anyway, you either create your own test implementations of all the Cinch required services, or use the defaults supplied, and then you must inject them in using Unity into Cinch.

Here is what your Unit Test project App.Config should look like:

<?xml version="1.0"?>
<configuration>

  <configSections>
    <section name="unity"
        type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
              Microsoft.Practices.Unity.Configuration" />
    <section name="log4net" 
      type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
    <section name="slf" type="Slf.Config.SlfConfigurationSection, slf"/>    
    
  </configSections>

  <!-- Unity Config Section -->
  <unity>
    <containers>
      <container>
        <types>

          <type
              type="Cinch.IUIVisualizerService, Cinch"
              mapTo="Cinch.TestUIVisualizerService, Cinch"/>

          <type
              type="Cinch.IMessageBoxService, Cinch"
              mapTo="Cinch.TestMessageBoxService, Cinch"/>

          <type
              type="Cinch.IOpenFileService, Cinch"
              mapTo="Cinch.TestOpenFileService, Cinch"/>

          <type
              type="Cinch.ISaveFileService, Cinch"
              mapTo="Cinch.TestSaveFileService, Cinch"/>

        </types>
      </container>
    </containers>
  </unity>

  <slf>
    <factories>
      <!-- configure single log4net factory, which will get all logging output -->
      <!-- Important: Set a reference to the log4net facade library 
           to make sure it will be available at runtime -->
      <factory type="SLF.Log4netFacade.Log4netLoggerFactory, SLF.Log4netFacade"/>
    </factories>
  </slf>


  <!-- configures log4net to write into a local file called "log.txt" -->
  <log4net>
    <!--  log4net uses the concept of 'appenders' to indicate where log messages are written to.
          Appenders can be files, the console, databases, SMTP and much more
    -->
    <appender name="MainAppender" type="log4net.Appender.FileAppender">
      <param name="File" value="log.txt" />
      <param name="AppendToFile" value="true" />
      <!--  log4net can optionally format the logged messages with a pattern. 
            This pattern string details what information
            is logged and the format it takes. A wide range of information 
            can be logged, including message, thread, identity and more,
            see the log4net documentation for details:
            http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
      -->
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%logger: %date [%thread] %-5level - %message %newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="MainAppender" />
    </root>
  </log4net>

</configuration>

This ensures that the default WPF service implementations are overridden by the Unit Test service implementations.

Exposed Services

So you have now seen that there is a Unity IOC container that is responsible for locating and loading the right services as dictated by the current App.Config file. Well, in Cinch, the story doesn't end there. You see, the thing is, within Cinch, the general idea is that there is a kind of all powerful ViewModel base class (Cinch.ViewModelBase) and providing you inherit from that, you will get some good stuff for free. Exposed services is one such thing.

You may be asking: Why do we need to do more stuff with the services? I thought that is what the Unity IOC container was doing for us. Well, that's half right, what the Unity IOC container does is get the current services (as dictated by the App.Config) into the Cinch.ViewModelBase. Which is all cool. The problem with the Unity IOC container is that if you try and request a service from it, it appears that it gives you a different instance each time. Which may be cool if your service implementations do not require any state, but in Cinch, some of the test services do require state. So this just didn't cut it. So what happens is that the Unity read in services are added to a static available property on Cinch.ViewModelBase. This property is a ServiceProvider, which is nothing more than a wrapper around a Dictionary really. So when you request a service from the Cinch.ViewModelBase using the Resolve<T> method you will see in a minute, the ServiceProvider will examine its internal Dictionary and return the single instance of the service. This ensures we are always working with the same instance of a service.

This diagram will help explain this a bit better.

And here is the relevant code from the Cinch.ViewModelBase class:

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 IIOCProvider iocProvider = null;
        private static ILogger logger;
        private static Boolean isInitialised = false;
        private static Action<IUIVisualizerService> setupVisualizer = null;

        private Boolean isCloseable = true;

        /// <summary>
        /// Service resolver for view models. Allows derived types to add/remove
        /// services from mapping.
        /// </summary>
        public static readonly ServiceProvider ServiceProvider = new ServiceProvider();

        /// <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)
        {
            if (iocProvider == null)
                throw new InvalidOperationException(
                    String.Format(
                        "ViewModelBase constructor requires " + 
                        "a IIOCProvider instance in order to work"));

            this.iocProvider = iocProvider;

            if (!ViewModelBase.isInitialised)
            {
                iocProvider.SetupContainer();
                FetchCoreServiceTypes();
            }

            //Register all decorated methods to the Mediator
            //Register all decorated methods to the Mediator
            Mediator.Instance.Register(this);
        }

        /// <summary>
        /// This resolves a service type and returns the implementation.
        /// </summary>
        /// <typeparam name="T">Type to resolve</typeparam>
        /// <returns>Implementation</returns>
        protected T Resolve<T>()
        {
            return ServiceProvider.Resolve<T>();
        }

        /// <summary>
        /// Delegate that is called when the services are injected
        /// </summary>
        public static Action<IUIVisualizerService> SetupVisualizer
        {
            get { return setupVisualizer; }
            set { setupVisualizer=value; }
        }
 
        /// <summary>
        /// Logger : The ILogger implementation in use
        /// </summary>
        public ILogger Logger
        {
            get { return logger; }
        }

        /// <summary>
        /// This method registers services with the service provider.
        /// </summary>
        private void FetchCoreServiceTypes()
        {
            try
            {
                ViewModelBase.isInitialised = false;

                //ILogger : Allows MessageBoxs to be shown 
                logger = (ILogger)this.iocProvider.GetTypeFromContainer<ILogger>();

                ServiceProvider.Add(typeof(ILogger), logger);

                 //IMessageBoxService : Allows MessageBoxs to be shown 
                IMessageBoxService messageBoxService =
                    (IMessageBoxService)
                     this.iocProvider.GetTypeFromContainer<imessageboxservice>();

                ServiceProvider.Add(typeof(IMessageBoxService), messageBoxService);

                //IOpenFileService : Allows Opening of files 
                IOpenFileService openFileService =
                    (IOpenFileService)
                     this.iocProvider.GetTypeFromContainer<iopenfileservice>();
                ServiceProvider.Add(typeof(IOpenFileService), openFileService);

                //ISaveFileService : Allows Saving of files 
                ISaveFileService saveFileService =
                    (ISaveFileService)
                     this.iocProvider.GetTypeFromContainer<isavefileservice>();
                ServiceProvider.Add(typeof(ISaveFileService), saveFileService);

                //IUIVisualizerService : Allows popup management
                IUIVisualizerService uiVisualizerService =
                   (IUIVisualizerService)
                    this.iocProvider.GetTypeFromContainer<iuivisualizerservice>();
                ServiceProvider.Add(typeof(IUIVisualizerService), uiVisualizerService);

                //call the callback delegate to setup IUIVisualizerService managed
                //windows
                if (SetupVisualizer != null)
                    SetupVisualizer(uiVisualizerService);

                ViewModelBase.isInitialised = true;

            }
            catch (Exception ex)
            {
                LogExceptionIfLoggerAvailable(ex);
            }
        }

     
        /// <summary>
        /// Logs a message if there is a ILoggerService available. And then throws
        /// new ApplicationException which should be caught somewhere external
        /// to this class
        /// </summary>
        /// <param name="ex">Exception to log</param>
        private static void LogExceptionIfLoggerAvailable(Exception ex)
        {
            if (logger != null)
                logger.Error("An error occurred", ex);

            throw new ApplicationException(ex.Message);
        }
    }
}

Cinch Services Available

Services are really nothing more than an interface that can be implemented any way you like. So to make a WPF implementation of a service, you would implement the service interface for WPF. To do a test service, you would implement the service for a Unit test, etc.

The following subsections shall outline what actual Test/WPF services are available.

Note: I did say WPF not Silverlight, Cinch is a WPF framework, it is not targeting Silverlight, I guess it could be made to do so, but that was not the intention of it.

Logging Service

By default, Cinch uses a ILogger based service, which by default uses the logging facade work called Simple Logging Facade (SLF), and makes use of the log4Net facade, so the log entries use the log4Net style, which can be configured through the App.Config.

If you do not like this and would rather use a different logging facility, all you have to do is inject a new ILogger implementation in Cinch using the IOC container of your choice.

Note: Cinch does not provide a test version of this service as it was deemed that both test and runtime code could use the same Logger implementation.

The ILogger service looks like this:

using System;

namespace Cinch
{
    /// <summary>
    /// This interface defines a interface that will allow 
    /// a ViewModel to output a Logmessage to whatever format 
    /// the consumer specifies when they provide a ILogger based
    /// service. Note Cinch supplies a SLF default ILogger based
    /// service, but a new one can be injected using any IOC mechanism
    /// you like
    /// </summary>
    public interface ILogger
    {
        void Error(Exception exception);
        void Error(object obj);
        void Error(string message);
        void Error(Exception exception, string message);
        void Error(string format, params object[] args);
        void Error(Exception exception, string format, params object[] args);
        void Error(IFormatProvider provider, string format, params object[] args);
        void Error(Exception exception, string format, 
                   IFormatProvider provider, params object[] args);
    }
}

MessageBox Service

  1. Available for: WPF UI Project
  2. Available for: Unit Test Project

Cinch provides a novel way of dealing with Unit Test service implementations. Whilst it is possible to use your favourite mocking framework (RhinoMocks/Moq etc.), sometimes that is not enough. Imagine that you have a section of code in a ViewModel, something like the following:

var messageBoxService = this.Resolve<imessageboxservice>();
 
if (messageBoxService.ShowYesNo("You sure",
    CustomDialogIcons.Question) == CustomDialogResults.Yes)
{
    if (messageBoxService.ShowYesNo("You totally sure",
        CustomDialogIcons.Question) == CustomDialogResults.Yes)
    {
        //DO IT
    }
}

Where we have an atomic bit of code within a ViewModel that needs to be fully tested by Unit Tests. Using Mocks, we could provide a Mock Cinch.IMessageBoxService service implementation. But this would not work as we would only be able to provide a single response, which is not the same as what the real WPF Cinch.IMessageBoxService would do, as the user would be free to use an actual MessageBox and may pick Yes/No/Cancel at random. So clearly, Mocks is not enough. We need a better idea.

So what Cinch does is to provide a Unit Test Cinch.IMessageBoxService service implementation which allows the Unit Test to enqueue the response Func<CustomDialogResults> (which are after all just delegates), which allows us to provide callback code that will be called by the ViewModel code. This allows us to do whatever the hell we want in the enqueued callback Func<CustomDialogResults>, as supplied by the unit tests.

This diagram may help to explain this concept a bit better.

So what happens is that the unit test enqueues all the responses that are required by using Func<CustomDialogResults> (which are the callback delegates) which are then called from the Unit Test implementation of the Cinch.IMessageBoxService service implementation.

Here is an example of what the Unit test implementation of the Cinch.IMessageBoxService service implementation looks like for a ShowYesNo() Cinch.IMessageBoxService service implementation method call:

/// <summary>
/// Returns the next Dequeue ShowYesNo response expected. See the tests for 
/// the Func callback expected values
/// </summary>
/// <param name="message">The message to be displayed.</param>
/// <param name="icon">The icon to be displayed.</param>
 
/// <returns>User selection.</returns>
public CustomDialogResults ShowYesNo(string message, CustomDialogIcons icon)
{
    if (ShowYesNoResponders.Count == 0)
        throw new ApplicationException(
            "TestMessageBoxService ShowYesNo method expects " + 
            "a Func<CustomDialogResults> callback \r\n" +
            "delegate to be enqueued for each Show call");
    else
    {
        Func<CustomDialogResults> responder = ShowYesNoResponders.Dequeue();
        return responder();
    }
}

It can be seen that the Unit Test implementation of the Cinch.IMessageBoxService service implementation for the ShowYesNo() method simply dequeues the next Func<CustomDialogResults> (which are after all just delegates) and calls the Func<CustomDialogResults> (which is queued up in the actual Unit test) and uses the result from the call to the Func<CustomDialogResults>.

Here is an example of how you might set up Unit Test code to enqueue the correct Func<CustomDialogResults> responses for the ViewModel code we saw above:

testMessageBoxService.ShowYesNoResponders.Enqueue
    (() =>
        {
            //return Yes for "Are sure" ViewModel prompt
            return CustomDialogResults.Yes;
        }
    );
 
testMessageBoxService.ShowYesNoResponders.Enqueue
    (() =>
        {
            //return Yes for "Are totally sure" ViewModel prompt
            return CustomDialogResults.Yes;
        }
    );

By using this method, we can guarantee we drive the ViewModel code through any test path we want to. It is a very powerful technique.

The Cinch.IMessageBoxService service interface looks like this:

/// <summary>
/// This interface defines a interface that will allow 
/// a ViewModel to show a messagebox
/// </summary>
public interface IMessageBoxService
{
    /// <summary>
    /// Shows an error message
    /// </summary>
 
    /// <param name="message">The error message</param>
    void ShowError(string message);
 
    /// <summary>
    /// Shows an information message
    /// </summary>
    /// <param name="message">The information message</param>
 
    void ShowInformation(string message);
 
    /// <summary>
    /// Shows an warning message
    /// </summary>
    /// <param name="message">The warning message</param>
    void ShowWarning(string message);
 
    /// <summary>
 
    /// Displays a Yes/No dialog and returns the user input.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
    /// <param name="icon">The icon to be displayed.</param>
 
    /// <returns>User selection.</returns>
    CustomDialogResults ShowYesNo(string message, CustomDialogIcons icon);
 
    /// <summary>
    /// Displays a Yes/No/Cancel dialog and returns the user input.
    /// </summary>
    /// <param name="message">The message to be displayed.</param>
 
    /// <param name="icon">The icon to be displayed.</param>
    /// <returns>User selection.</returns>
    CustomDialogResults ShowYesNoCancel(string message, CustomDialogIcons icon);
 
    /// <summary>
    /// Displays a OK/Cancel dialog and returns the user input.
    /// </summary>
 
    /// <param name="message">The message to be displayed.</param>
    /// <param name="icon">The icon to be displayed.</param>
    /// <returns>User selection.</returns>
 
    CustomDialogResults ShowOkCancel(string message, CustomDialogIcons icon);
}

Open File Service

  1. Available for: WPF UI Project
  2. Available for: Unit Test Project

This works roughly the same way as just outlined above for the Cinch.IMessageBoxService service, but this time, the enqueued values are Queue<Func<bool?>>. Which means you can simulate a file being opened from within the unit test, by enquing the required Func<bool?> values as needed by the ViewModel code currently under test.

The Cinch.IOpenFileService service interface looks like this:

/// <summary>
/// This interface defines a interface that will allow 
/// a ViewModel to open a file
/// </summary>
public interface IOpenFileService
{
    /// <summary>
    /// FileName
    /// </summary>
 
    String FileName { get; set; }
 
    /// <summary>
    /// Filter
    /// </summary>
    String Filter { get; set; }
 
    /// <summary>
    /// Filter
    /// </summary>
    String InitialDirectory { get; set; }
 
    /// <summary>
 
    /// This method should show a window that allows a file to be selected
    /// </summary>
    /// <param name="owner">The owner window of the dialog</param>
    /// <returns>A bool from the ShowDialog call</returns>
    bool? ShowDialog(Window owner);
}

Save File Service

  1. Available for: WPF UI Project
  2. Available for: Unit Test Project

This works roughly the same way as just outlined above for the Cinch.IMessageBoxService service, but this time, the enqueued values are Queue<Func<bool?>>. Which means you can simulate a file being saved from within the unit test, by enquing the required Func<bool?> values as needed by the ViewModel code currently under test.

For example, you may wish to actually create a file in the enqueued Func<bool?> that you queued up in the unit test, and only then return true, which a ViewModel can then check, and proceed to use the file that you actually saved within the Unit Test.

For example, you could do something like this inside a Unit test:

testSaveFileService.ShowDialogResponders.Enqueue
    (() =>
      {
        String path = @"c:\test.txt";
        if (!File.Exists(path)) 
        {
            // Create a file to write to.
            using (StreamWriter sw = File.CreateText(path)) 
            {
                sw.WriteLine("Hello");
                sw.WriteLine("Cinch");
            }    
        }
 
        testSaveFileService.FileName = path ;
        return true;
      }
    );

The Cinch.ISaveFileService service interface looks like this:

/// <summary>
/// This interface defines a interface that will allow 
/// a ViewModel to save a file
/// </summary>
public interface ISaveFileService
{
    /// <summary>
    /// FileName
    /// </summary>
    Boolean OverwritePrompt { get; set; }
 
    /// <summary>
 
    /// FileName
    /// </summary>
    String FileName { get; set; }
 
    /// <summary>
    /// Filter
    /// </summary>
    String Filter { get; set; }
 
    /// <summary>
    /// Filter
    /// </summary>
 
    String InitialDirectory { get; set; }
 
    /// <summary>
    /// This method should show a window that allows a file to be saved
    /// </summary>
    /// <param name="owner">The owner window of the dialog</param>
    /// <returns>A bool from the ShowDialog call</returns>
 
    bool? ShowDialog(Window owner);
}

Popup Window Service

  • Available for: WPF UI Project (but not inside Cinch code, see demo app)
  • Available for: Unit Test Project

I do not know about you, but we are in the middle of a very large WPF project at work, and although I am not a fan of popup windows, we do have some nonetheless. Popups kind of don't play well with the normal way that most folk do MVVM. Most folk would make a View a UserControl that has a ViewModel as a DataContext. Which is cool. But occasionally, we need to show a popup and have it edit some object within the current ViewModel or allow the user to cancel the edit.

How Cinch does this, is it provides a service called Cinch.IUIVisualizerService which is a fairly complex beast. But it has to be. Here is the basic idea:

The WPF app that has the popups must provide the popups to the Cinch.IUIVisualizerService (the attached demo app shows this). This provided implementation is expected to be injected via Unity into Cinch.

The Cinch.IUIVisualizerService WPF implementation as supplied in the demo app supplies popup window types that are not available to any other project except the actual WPF app. Which is why Cinch can not provide these popups to the Cinch.IUIVisualizerService WPF implementation. So what has to happen is that the WPF app must tell the Cinch.IUIVisualizerService WPF implementation what popups are expected. This can be done in the constructor of the app's main window as follows, via a callback delegate that Cinch calls when the Cinch.IUIVisualizerService service has been injected:

namespace MVVM.Demo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            //register known windows via callback from ViewModelBase
            //when services are ready
            ViewModelBase.SetupVisualizer = (x) =>
                {
                    x.Register("AddEditOrderPopup", typeof(AddEditOrderPopup));
                };

            this.DataContext = new MainWindowViewModel();
            InitializeComponent();
        }
    }
}

The demo app supplied WPF implementation of the Cinch.IUIVisualizerService service looks like this:

using System;
using System.Collections.Generic;
using System.Windows;
 
using Cinch;
 
namespace MVVM.Demo
{
    /// <summary>
    /// This class implements the IUIVisualizerService for WPF purposes.
    /// This implementation HAD TO be in the Main interface project, as
    /// it needs to know about Popup windows that are not known about in 
    /// the ViewModel or Cinch projects.
    /// </summary>
 
    public class WPFUIVisualizerService : Cinch.IUIVisualizerService
    {
        #region Data
        private readonly Dictionary<string, Type> _registeredWindows;
        #endregion
 
        #region Ctor
        /// <summary>
        /// Constructor
        /// </summary>
        public WPFUIVisualizerService()
        {
            _registeredWindows = new Dictionary<string, Type>();
 
            //register known windows
            Register("AddEditOrderPopup", typeof(AddEditOrderPopup));
        }
        #endregion
 
        #region Public Methods
        /// <summary>
 
        /// Registers a collection of entries
        /// </summary>
        /// <param name="startupData"></param>
        public void Register(Dictionary<string, Type> startupData)
        {
            foreach (var entry in startupData)
                Register(entry.Key, entry.Value);
        }
 
        /// <summary>
        /// Registers a type through a key.
        /// </summary>
 
        /// <param name="key">Key for the UI dialog</param>
        /// <param name="winType">Type which implements dialog</param>
        public void Register(string key, Type winType)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");
            if (winType == null)
                throw new ArgumentNullException("winType");
            if (!typeof(Window).IsAssignableFrom(winType))
                throw new ArgumentException("winType must be of type Window");
 
            lock (_registeredWindows)
            {
                _registeredWindows.Add(key, winType);
            }
        }
 
        /// <summary>
        /// This unregisters a type and removes it from the mapping
        /// </summary>
 
        /// <param name="key">Key to remove</param>
        /// <returns>True/False success</returns>
        public bool Unregister(string key)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");
 
            lock (_registeredWindows)
            {
                return _registeredWindows.Remove(key);
            }
        }
 
        /// <summary>
        /// This method displays a modaless dialog associated with the given key.
        /// </summary>
 
        /// <param name="key">Key previously registered with the UI controller.</param>
        /// <param name="state">Object state to associate with the dialog</param>
        /// <param name="setOwner">Set the owner of the window</param>
 
        /// <param name="completedProc">Callback used when UI closes (may be null)</param>
        /// <returns>True/False if UI is displayed</returns>
        public bool Show(string key, object state, bool setOwner, 
            EventHandler<UICompletedEventArgs> completedProc)
        {
            Window win = CreateWindow(key, state, setOwner, completedProc, false);
            if (win != null)
            {
                win.Show();
                return true;
            }
            return false;
        }
 
        /// <summary>
 
        /// This method displays a modal dialog associated with the given key.
        /// </summary>
        /// <param name="key">Key previously registered with the UI controller.</param>
        /// <param name="state">Object state to associate with the dialog</param>
        /// <returns>True/False if UI is displayed.</returns>
 
        public bool? ShowDialog(string key, object state)
        {
            Window win = CreateWindow(key, state, true, null, true);
            if (win != null)
                return win.ShowDialog();
 
            return false;
        }
        #endregion
 
        #region Private Methods
        /// <summary>
        /// This creates the WPF window from a key.
        /// </summary>
        /// <param name="key">Key</param>
        /// <param name="dataContext">DataContext (state) object</param>
 
        /// <param name="setOwner">True/False to set ownership to MainWindow</param>
        /// <param name="completedProc">Callback</param>
        /// <param name="isModal">True if this is a ShowDialog request</param>
 
        /// <returns>Success code</returns>
        private Window CreateWindow(string key, object dataContext, bool setOwner, 
            EventHandler<UICompletedEventArgs> completedProc, bool isModal)
        {
            if (string.IsNullOrEmpty(key))
                throw new ArgumentNullException("key");
 
            Type winType;
            lock (_registeredWindows)
            {
                if (!_registeredWindows.TryGetValue(key, out winType))
                    return null;
            }
 
            var win = (Window)Activator.CreateInstance(winType);
            win.DataContext = dataContext;
            if (setOwner && Application.Current != null)
                win.Owner = Application.Current.MainWindow;
 
            if (dataContext != null)
            {
                var bvm = dataContext as ViewModelBase;
                if (bvm != null)
                {
                    if (isModal)
                    {
                        bvm.CloseRequest += ((s, e) =>
                        {
                            try
                            {
                                win.DialogResult = e.Result;
                            }
                            catch (InvalidOperationException)
                            {
                                win.Close();
                            }
                        });
                    }
                    else
                    {
                        bvm.CloseRequest += ((s, e) => win.Close());
                    }
                    bvm.ActivateRequest += ((s, e) => win.Activate());
                }
            }
 
            if (completedProc != null)
            {
                win.Closed +=
                    (s, e) =>
 
                        completedProc
                        (this,new UICompletedEventArgs 
                            {   State = dataContext, 
                                Result = (isModal) ? win.DialogResult : null 
                            }
                        );
 
            }
 
            return win;
        }
        #endregion
    }
}

Its job is to set the newly requested popup window to have a DataContext set to some object, and also to listen to close commands coming from the launching ViewModel which instructs the popup to close.

So to use this service from a ViewModel to show a popup and set its DataContext, we would do something like the following:

addEditOrderVM.CurrentViewMode = ViewMode.AddMode;
addEditOrderVM.CurrentCustomer = CurrentCustomer;
bool? result = uiVisualizerService.ShowDialog("AddEditOrderPopup", addEditOrderVM);
 
if (result.HasValue && result.Value)
{
    CloseActivePopUpCommand.Execute(true);
}

It can be seen that this ViewModel code snippet is using one of the names of the registered (from the WPF app's implementation of Cinch.IUIVisualizerService) popup windows, and then showing the popup modally, and waiting for a DialogResult (bool?), and if the DialogResult was true, the ViewModel closes the popup.

Note that we are able to set the DialogResult value we would like returned when CloseActivePopupCommand is executed, which may be handy if you programmatically want to close the active popup rather than let the user use the button that is linked to the CloseActivePopUpCommand, which will always return true as shown below.

This is all cool, so we can now show popups from a ViewModel, set a DataContext, listen for a DialogResult, and then close the popup. Sounds cool. There is however a trick or two you need to know. These are as follows:

Tricks When Working With Popups and Cinch

Following these simple rules should help:

Make sure your Save and Cancel buttons have the IsDefault and IsCancel set like

<Button Content="Save" IsDefault="True" 
  Command="{Binding CloseActivePopUpCommand}"
  CommandParameter="True"/>
 
<Button Content="Cancel" IsCancel="True"/>

The Cinch.IUIVisualizerService looks like this:

/// <summary>
/// This interface defines a UI controller which can be used to display dialogs
/// in either modal or modaless form from a ViewModel.
/// </summary>
public interface IUIVisualizerService
{
    /// <summary>
    /// Registers a type through a key.
    /// </summary>
 
    /// <param name="key">Key for the UI dialog</param>
    /// <param name="winType">Type which implements dialog</param>
    void Register(string key, Type winType);
 
    /// <summary>
    /// This unregisters a type and removes it from the mapping
    /// </summary>
 
    /// <param name="key">Key to remove</param>
    /// <returns>True/False success</returns>
    bool Unregister(string key);
 
    /// <summary>
    /// This method displays a modaless dialog associated with the given key.
    /// </summary>
 
    /// <param name="key">Key previously registered with the UI controller.</param>
    /// <param name="state">Object state to associate with the dialog</param>
    /// <param name="setOwner">Set the owner of the window</param>
 
    /// <param name="completedProc">Callback used when UI closes (may be null)</param>
    /// <returns>True/False if UI is displayed</returns>
    bool Show(string key, object state, bool setOwner, 
        EventHandler<UICompletedEventArgs> completedProc);
 
    /// <summary>
 
    /// This method displays a modal dialog associated with the given key.
    /// </summary>
    /// <param name="key">Key previously registered with the UI controller.</param>
    /// <param name="state">Object state to associate with the dialog</param>
    /// <returns>True/False if UI is displayed.</returns>
 
    bool? ShowDialog(string key, object state);
}

If your app doesn't use popups, you can:

  1. Simply provide a dummy implementation of this service in your app and ensure that it is injected into Cinch using Unity
  2. Edit the Cinch.ViewModelBase class to remove all references of IUIVisualizerService

Threading Helpers

Threading is one of those things that we don't have to do that often (or maybe you do), but every time we need to do it again, it seems to bite our ass all over again. To this end, Cinch provides a couple of useful threading helper classes which are described below.

Dispatcher Extension Methods

Cinch contains several extension methods that are quite useful when working with the Dispatcher; these extension methods allow the user of the extension method to invoke a block of code using the correct UI Dispatcher thread, and optionally at a given DispatcherPriority. It is fairly simple and very easy to use. Here is the entire code for the Dispatcher Extension Methods:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
 
namespace Cinch
{
    /// <summary>
    /// Provides a set of commonly used Dispatcher extension methods
    /// </summary>
    public static class DispatcherExtensions
    {
        #region Dispatcher Extensions
        /// <summary>
        /// A simple threading extension method, to invoke a delegate
        /// on the correct thread if it is not currently on the correct thread
        /// which can be used with DispatcherObject types.
        /// </summary>
 
        /// <param name="dispatcher">The Dispatcher object on which to 
        /// perform the Invoke</param>
        /// <param name="action">The delegate to run</param>
        /// <param name="priority">The DispatcherPriority for the invoke.</param>
 
        public static void InvokeIfRequired(this Dispatcher dispatcher,
            Action action, DispatcherPriority priority)
        {
            if (!dispatcher.CheckAccess())
            {
                dispatcher.Invoke(priority, action);
            }
            else
            {
                action();
            }
        }
 
        /// <summary>
        /// A simple threading extension method, to invoke a delegate
        /// on the correct thread if it is not currently on the correct thread
        /// which can be used with DispatcherObject types.
        /// </summary>
        /// <param name="dispatcher">The Dispatcher object on which to 
        /// perform the Invoke</param>
        /// <param name="action">The delegate to run</param>
 
        public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
        {
            if (!dispatcher.CheckAccess())
            {
                dispatcher.Invoke(DispatcherPriority.Normal, action);
            }
            else
            {
                action();
            }
        }
 
        /// <summary>
        /// A simple threading extension method, to invoke a delegate
        /// on the correct thread if it is not currently on the correct thread
        /// which can be used with DispatcherObject types.
        /// </summary>
        /// <param name="dispatcher">The Dispatcher object on which to 
        /// perform the Invoke</param>
        /// <param name="action">The delegate to run</param>
 
        public static void InvokeInBackgroundIfRequired(
            this Dispatcher dispatcher, 
            Action action)
        {
            if (!dispatcher.CheckAccess())
            {
                dispatcher.Invoke(DispatcherPriority.Background, action);
            }
            else
            {
                action();
            }
        }
 
        /// <summary>
        /// A simple threading extension method, to invoke a delegate
        /// on the correct thread asynchronously if it is not currently 
        /// on the correct thread which can be used with DispatcherObject types.
        /// </summary>
        /// <param name="dispatcher">The Dispatcher object on which to 
        /// perform the Invoke</param>
        /// <param name="action">The delegate to run</param>
 
        public static void InvokeAsynchronouslyInBackground(
            this Dispatcher dispatcher, Action action)
        {
            if (dispatcher != null)
                dispatcher.BeginInvoke(DispatcherPriority.Background, action);
            else
                action();
        }
        #endregion
    }
}

To use this Extension Method is dead easy, you would just do something like this:

Dispatcher.InvokeIfRequired(() =>
{
    //run some code on the correct UI Dispatcher thread
    //at the DispatcherPriority stated
 
},DispatcherPriority.Background);

Obviously, if you wanted to do something with the Dispatcher for a View from a ViewModel, you would need to use the CurrentDispatcher static property.

App.DoEvents

Within WPF, there is no App.DoEvents() that some WinForms converts might be expecting. For those that have not used the the old WinForms App.DoEvents(), what that method used to do was force the message pump to process all the queued messages. This would sometimes help with things like selection changes, pending events, etc.

Basically, it was quite a useful feature, but as I say, there is nothing like this supplied out of the box in WPF. As luck would have it, it is not too much bother to fashion your own using the Dispatcher (the place where messages are queued) and a DispatcherFrame. Cinch provides two flavours of App.DoEvents:

  • One where the user specifies a DispatcherPriority for the lower limit that all pending Dispatcher messages should be effectively pumped.
  • One where all Dispatcher messages should be effectively pumped.

The following code shows how this works:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.Security.Permissions;
 
namespace Cinch
{
    public static class ApplicationHelper
    {
        #region DoEvents
        /// <summary>
        /// Forces the WPF message pump to process all enqueued messages
        /// that are above the input parameter DispatcherPriority.
        /// </summary>
        /// <param name="priority">The DispatcherPriority to use
        /// as the lowest level of messages to get processed</param>
        [SecurityPermissionAttribute(SecurityAction.Demand,
            Flags = SecurityPermissionFlag.UnmanagedCode)]
        public static void DoEvents(DispatcherPriority priority)
        {
            DispatcherFrame frame = new DispatcherFrame();
            DispatcherOperation dispatcherOperation = 
                Dispatcher.CurrentDispatcher.BeginInvoke(priority, 
                    new DispatcherOperationCallback(ExitFrameOperation), frame);
            
            Dispatcher.PushFrame(frame);
 
            if (dispatcherOperation.Status != DispatcherOperationStatus.Completed)
            {
                dispatcherOperation.Abort();
            }
        }
  
        /// <summary>
        /// Forces the WPF message pump to process all enqueued messages
        /// that are DispatcherPriority.Background or above
        /// </summary>
        [SecurityPermissionAttribute(SecurityAction.Demand,
            Flags = SecurityPermissionFlag.UnmanagedCode)]
        public static void DoEvents()
        {
            DoEvents(DispatcherPriority.Background);
        }
 
        /// <summary>
        /// Stops the dispatcher from continuing
        /// </summary>
        private static object ExitFrameOperation(object obj)
        {
            ((DispatcherFrame)obj).Continue = false;
            return null;
        }
        #endregion
    }
}

So to use this WPF App.DoEvents(), all you need to do is, call it like the following:

ApplicationHelper.DoEvents();

Or to process all message of a particular DispatcherPriority of above, use the following:

ApplicationHelper.DoEvents(DispatcherPriority.Background);

Background Tasks

One difficulty I have had when working with large DataSets and MVVM was how to UnitTest and synchronize using background tasks. Some use the ThreadPool (or the very cool SmartThreadPool hosted here at CodeProject), others use BackgroundWorker. I will try and use what fits the job generally. However, for Cinch, I made the decision to include a BackgroundWorker wrapper class that I found on the internet. I did make a few changes to it to change the completed callback ordering. I have to say it is quite neat.

Is it called BackgroundTaskManager<T> and takes a generic which indicates the return value when the background task is run. So ideally, you would only do an operation that returns a single type in the background, and use that as the result. Of course, you could have multiple BackgroundTaskManager<T> objects within a single ViewModel, each doing a different background activity.

What it allows you to do is hook up a Func<T> taskFunc, Action<T> completionAction within the constructor. The Func<T> taskFunc is used wired up against the BackgroundWorker.DoWork() method, whilst the Action<T> completionAction is called when the BackgroundWorker.Completed event is raised.

One thing that I added was a AutoResetEvent WaitHandle property, which a Unit test may set to allow the Unit test to wait for a signal on the AutoResetEvent WaitHandle property supplied via the Unit test. More on this later.

For now, let's continue to look at what this BackgroundTaskManager<T> class looks like; here it is:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace MVVM.ViewModels
{
    public class BackgroundTaskManager<T>
    {
        #region Data
        private Func<T> TaskFunc { get; set; }
        private Action<T> CompletionAction { get; set; }
        #endregion
 
        #region Ctor
        /// <summary>
        /// Constructs a new BackgroundTaskManager with
        /// the function to run, and the action to call when the function to run
        /// completes
        /// </summary>
 
        /// <param name="taskFunc">The function to run in the background</param>
        /// <param name="completionAction">The completed action to call
        /// when the background function completes</param>
        public BackgroundTaskManager(Func<T> taskFunc, Action<T> completionAction)
        {
            this.TaskFunc = taskFunc;
            this.CompletionAction = completionAction;
        }
        #endregion
 
        #region Public Properties
        /// <summary>
 
        /// Event invoked when a background task is started.
        /// </summary>
        [SuppressMessage("Microsoft.Usage", 
            "CA2211:NonConstantFieldsShouldNotBeVisible",
            Justification = "Add/remove is thread-safe for events in .NET.")]
        public EventHandler<EventArgs> BackgroundTaskStarted;
 
        /// <summary>
        /// Event invoked when a background task completes.
        /// </summary>
        [SuppressMessage("Microsoft.Usage", 
            "CA2211:NonConstantFieldsShouldNotBeVisible",
            Justification = "Add/remove is thread-safe for events in .NET.")]
        public EventHandler<EventArgs> BackgroundTaskCompleted;
 
        /// <summary>
 
        /// Allows the Unit test to be notified on Task completion
        /// </summary>
        public AutoResetEvent CompletionWaitHandle { get; set; }
        #endregion
        
        #region Public Methods
        /// <summary>
        /// Runs a task function on a background thread; 
        /// invokes a completion action on the main thread.
        /// </summary>
        public void RunBackgroundTask()
        {
            // Create a BackgroundWorker instance
            var backgroundWorker = new BackgroundWorker();
 
            // Attach to its DoWork event to run the task function and capture the result
            backgroundWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
            {
                e.Result = TaskFunc();
            };
 
            // Attach to its RunWorkerCompleted event to run the completion action
            backgroundWorker.RunWorkerCompleted += 
                delegate(object sender, RunWorkerCompletedEventArgs e)
            {
                // Call the completion action
                CompletionAction((T)e.Result);
                
                // Invoke the BackgroundTaskCompleted event
                var backgroundTaskFinishedHandler = BackgroundTaskCompleted;
                if (null != backgroundTaskFinishedHandler)
                {
                    backgroundTaskFinishedHandler.Invoke(null, EventArgs.Empty);
                }
            };
 
            // Invoke the BackgroundTaskStarted event
            var backgroundTaskStartedHandler = BackgroundTaskStarted;
            if (null != backgroundTaskStartedHandler)
            {
                backgroundTaskStartedHandler.Invoke(null, EventArgs.Empty);
            }
 
            // Run the BackgroundWorker asynchronously
            backgroundWorker.RunWorkerAsync();
        }
        #endregion
    }
}

So how do you use one of these BackgroundTaskManager<T> classes? Well, it is fairly easy actually. I am not going to cover unit testing yet, as that is the subject for another article. For now, I will just show you how to use it from your ViewModel, which is done as follows:

  1. Create a private property
  2. Create a public property to allow Unit tests access to the BackgroundTaskManager<T>
  3. Wire up the BackgroundTaskManager<T> Func<T> taskFunc, Action<T> completionAction within the constructor of BackgroundTaskManager<T>
  4. Have some method do something with the BackgroundTaskManager<T>

This is all demonstrated in the code snippet shown below. We will see more of this when we come to create a ViewModel using Cinch and Unit testing with Cinch in later articles.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;
using System.Windows.Data;
 
using Cinch;
using MVVM.Models;
using MVVM.DataAccess;
 
namespace MVVM.ViewModels
{
    public class SomeViewModel : Cinch.WorkspaceViewModel
    {
        //background workers
        private 
          BackgroundTaskManager<DispatcherNotifiedObservableCollection<OrderModel>> 
          bgWorker = null;

        public AddEditCustomerViewModel()
        {
            //setup background worker
            SetUpBackgroundWorker();
        }
        
        /// <summary>
        /// Background worker which lazy fetches 
        /// Customer Orders
        /// </summary>
        public BackgroundTaskManager
                 <DispatcherNotifiedObservableCollection<OrderModel>> BgWorker
        {
            get { return bgWorker; }
            set
            {
                bgWorker = value;
                OnPropertyChanged(() => BgWorker);
            }
        }
 
        /// <summary>
        /// Setup backgrounder worker Task/Completion action
        /// to fetch Orders for Customers
        /// </summary>
 
        private void SetUpBackgroundWorker()
        {
            bgWorker = new BackgroundTaskManager
                         <DispatcherNotifiedObservableCollection<OrderModel>>(
                () =>
                {
                    return new DispatcherNotifiedObservableCollection<OrderModel>(
                        DataAccess.DataService.FetchAllOrders(
                            CurrentCustomer.CustomerId.DataValue).ConvertAll(
                                new Converter<Order, OrderModel>(
                                      OrderModel.OrderToOrderModel)));
                },
                (result) =>
                {
 
                    CurrentCustomer.Orders = result;
                    if (customerOrdersView != null)
                        customerOrdersView.CurrentChanged -=
                            CustomerOrdersView_CurrentChanged;

                    customerOrdersView =
                        CollectionViewSource.GetDefaultView(CurrentCustomer.Orders);
                    customerOrdersView.CurrentChanged +=
                        CustomerOrdersView_CurrentChanged;
                    customerOrdersView.MoveCurrentToPosition(-1);
 
                    HasOrders = CurrentCustomer.Orders.Count > 0;
                });
        }

        /// <summary>
        /// Fetches all Orders for customer using BackgroundTaskManager<T>
        /// </summary>
        private void LazyFetchOrdersForCustomer()
        {
            bgWorker.RunBackgroundTask();
        }
    }
}

ObservableCollection

Another thing that occurs occasionally is that you may have an ObservableCollection that has items added to it that need to be marshaled back to a UI thread to make use of the new items. Cinch uses the following code to achieve this:

/// <summary>
/// This class provides an ObservableCollection which supports the 
/// Dispatcher thread marshalling for added items. 
/// 
/// This class does not take support any thread sycnhronization of
/// adding items using multiple threads, that level of thread synchronization
/// is left to the user. This class simply marshalls the CollectionChanged
/// call to the correct Dispatcher thread
/// </summary>
/// <typeparam name="T">Type this collection holds</typeparam>
public class DispatcherNotifiedObservableCollection<T> : ObservableCollection<T>
{
    #region Ctors
 
    public DispatcherNotifiedObservableCollection()
        : base()
    {
    }
 
    public DispatcherNotifiedObservableCollection(List<T> list)
        : base(list)
    {
    }
 
    public DispatcherNotifiedObservableCollection(IEnumerable<T> collection) 
        : base(collection)
    {
 
    }
    #endregion
 
    #region Overrides
    /// <summary>
    /// Occurs when an item is added, removed, changed, moved, 
    /// or the entire list is refreshed.
    /// </summary>
    public override event NotifyCollectionChangedEventHandler CollectionChanged;
 
    /// <summary>
    /// Raises the <see cref="E:System.Collections.ObjectModel.
    /// ObservableCollection`1.CollectionChanged"/> 
    /// event with the provided arguments.
    /// </summary>
    /// <param name="e">Arguments of the event being raised.</param>
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        var eh = CollectionChanged;
        if (eh != null)
        {
            Dispatcher dispatcher = 
                (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
                 let dpo = nh.Target as DispatcherObject
                 where dpo != null
                 select dpo.Dispatcher).FirstOrDefault();
 
            if (dispatcher != null && dispatcher.CheckAccess() == false)
            {
                dispatcher.Invoke(DispatcherPriority.DataBind, 
                    (Action)(() => OnCollectionChanged(e)));
            }
            else
            {
                foreach (NotifyCollectionChangedEventHandler nh 
                        in eh.GetInvocationList())
                    nh.Invoke(this, e);
            }
        }
    }
    #endregion
}

Doing MenuItems the MVVM Way

Newbies to the MVVM pattern will probably struggle with doing menus using the MVVM pattern. Which is a shame as they are actually fairly simple and quite easy to tame. After all, they are simply a hierarchical structure (think tree) that allows some code to be run either using a Click or using an ICommand. They actually sound (at least operationally) a lot like Button objects, which also have a Click and can use an ICommand, and we know how to deal with those. We just bind their Command property to a ViewModel exposed ICommand property. So why should menus be any different? It turns out they are not, it's just how we create the collection of menus and Style them in the View that represents any difficulty at all.

Representing MenuItems in the ViewModel

Let's start with having a look at how we might represent a ViewModel friendly MenuItem object, shall we? We could do something like the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
 
namespace Cinch
{
    /// <summary>
    /// Provides a mechanism for constructing MenuItems
    /// within a ViewModel
    /// </summary>
    public class WPFMenuItem
    {
        #region Public Properties
        public String Text { get; set; }
        public String IconUrl { get; set; }
        public List<WPFMenuItem> Children { get; private set; }
        public SimpleCommand Command { get; set; }
        #endregion
 
        #region Ctor
        public WPFMenuItem(string item)
        {
            Text = item;
            Children = new List<WPFMenuItem>();
        }
        #endregion
    }
}

Simple enough, right? So how do we expose a menu from a ViewModel that the View can use? Let's look at that next.

Exposing MenuItems From the ViewModel

Well, this too is actually quite simple. We just expose a property of our ViewModel for the MenuItems we want to expose. Here is an example property within a ViewModel.

/// <summary>
/// Returns the bindbable Menu options
/// </summary>
public List<WPFMenuItem> OrderMenuOptions
{
    get
    {
        return CreateMenus();
    }
}

The only part that really needs explaining is the call to the CreateMenus() method. This is nothing clever; it just creates the MenuItems to expose from the ViewModel. Here is an example of what this might look like:

/// <summary>
/// Creates and returns the menu items
/// </summary>
private List<WPFMenuItem> CreateMenus()
{
    var menu = new List<WPFMenuItem>();
 
    var miAddOrder = new WPFMenuItem("Add Order");
    miAddOrder.Command = AddOrderCommand;
    menu.Add(miAddOrder);
 
    var miEditOrder = new WPFMenuItem("Edit Order");
    miEditOrder.Command = EditOrderCommand;
    menu.Add(miEditOrder);
 
    var miDeleteOrder = new WPFMenuItem("Delete Order");
    miDeleteOrder.Command = DeleteOrderCommand;
    menu.Add(miDeleteOrder);
 
    return menu;

}

You can see that in this method we are not only creating the structure of the MenuItems but we are also wiring up the MenuItems to the correct ViewModel available ICommands. It is that easy.

Rendering the ViewModel Exposed MenuItems Within the View

The last part of the puzzle is how to render the ViewModel exposed MenuItems within a particular View. Again, this is not hard. This can be achieved by firstly declaring a Menu in the View and binding that to the ViewModels exposed MenuItems:

<Menu x:Name="menu" Margin="0,0,0,0" 
      Height="Auto" Foreground="White"
      ItemContainerStyle="{StaticResource ContextMenuItemStyle}"
      ItemsSource="{Binding MenuOptions}"
      VerticalAlignment="Top" Background="#FF000000">
</Menu>

Where each individual MenuItem is styled using the following Style:

<Style x:Key="ContextMenuItemStyle">
    <Setter Property="MenuItem.Header" Value="{Binding Text}"/>
    <Setter Property="MenuItem.ItemsSource" Value="{Binding Children}"/>
    <Setter Property="MenuItem.Command" Value="{Binding Command}" />
    <Setter Property="MenuItem.Icon" Value="{Binding IconUrl, 
        Converter={StaticResource MenuIconConv}}" />
</Style>

Actually, one thing that lots of people have requested is the ability to create a separator. Now I did not allow for this, but luckily, a Cinch user alerted me to a cool way to do this. So you would have something like this in your XAML:

<local:SeparatorStyleSelector x:Key="separatorSelector" />
<Style x:Key="ContextMenuItemStyle">
     <Setter Property="MenuItem.Header" Value="{Binding Text}"/>
     <Setter Property="MenuItem.ItemsSource" Value="{Binding Children}"/>
     <Setter Property="MenuItem.Command" Value="{Binding Command}" />
     <Setter Property="MenuItem.Icon" Value="{Binding IconUrl, 
        Converter={StaticResource MenuIconConv}}" />
</Style>

<Style x:Key="SeparatorStyle" TargetType="{x:Type MenuItem}">
     <Setter Property="Template">
    <Setter.Value>
           <ControlTemplate>
                  <Separator />
           </ControlTemplate>
        </Setter.Value>
     </Setter>
</Style>

Where you make use of the following StyleSelector to create an alternative Style for Seperators.

public class SeparatorStyleSelector : StyleSelector
{
    public override Style SelectStyle(object item, DependencyObject container)
    {
        if (item is ViewModels.MenuItem)
        {
            ViewModels.MenuItem mi =
                item as ViewModels.MenuItem;
            if (mi.Text.Equals("--", StringComparison.OrdinalIgnoreCase))
            {
                return (Style)((FrameworkElement)
                          container).FindResource("SeparatorStyle");
            }
            else
            {
                return (Style)((FrameworkElement)
                         container).FindResource("ContextMenuItemStyle");
            }
        }
        return null;
    }
}

The full entry can be found at one of the new Cinch article forum entries http://www.codeproject.com/Messages/3555500/Separator-menu-item.aspx.

Closeable ViewModels

I don't know how many of the readers of this article are new to WPF and have come from WinForms, or how many of you are actually doing WPF, but I can tell you one thing. When WPF came out, it did not come with a way to do MDI interfaces out of the box. In fact, if you look at Expression Blend which is a Microsoft WPF tool for working with WPF, which by the way was also written in WPF, you will see that it looks radically different from the previous Microsoft developer tools such as Visual Studio. Expression Blend is a single window application that uses some simple tricks to manage the content. These tricks are really clever layout such as using lots of expanders/tabs. It is however a one window app.

Most WPF apps you see out there are also one window apps. When I do a new WPF app, I try and make it a one window app. Of course, sometimes popups are hard to avoid. In fact, the demo code has a popup which you will see in a later article.

But for now, let us imagine we want to build a one window app using tabs. How do we do that the MVVM way?

Let's have a look into that, shall we?

Cinch actually provides a base ViewModel called WorkspaceViewModel, which provides a single Closed event, which can be used as a base class for your own ViewModels. Here is the code for that:

using System;
 
namespace Cinch
{
    /// <summary>
    /// This ViewModelBase subclass requests to be removed 
    /// from the UI when its CloseWorkSpace executes.
    /// This class is abstract.
    /// </summary>
    public abstract class WorkspaceViewModel : ViewModelBase
    {
        #region Data
 
        private SimpleCommand closeWorkSpaceCommand;
        private Boolean isCloseable = true;
 
        #endregion 
 
        #region Constructor
 
        protected WorkspaceViewModel()
        {
            //This is used for popup control only
            closeWorkSpaceCommand = new SimpleCommand
            {
                CanExecuteDelegate = x => true,
                ExecuteDelegate = x => ExecuteCloseWorkSpaceCommand()
            };
        }
 
        #endregion // Constructor
 
        #region Public Properties
 
        /// <summary>
 
        /// Returns the command that, when invoked, attempts
        /// to remove this workspace from the user interface.
        /// </summary>
        public SimpleCommand CloseWorkSpaceCommand
        {
            get
            {
                return closeWorkSpaceCommand;
            }
        }
 
        public Boolean IsCloseable
        {
            get { return isCloseable; }
            set
            {
                isCloseable = value;
                OnPropertyChanged(() => IsCloseable);
            }
        }
 
        #endregion // CloseCommand
 
        #region Private Methods
 
        /// <summary>
        /// Executes the CloseWorkSpace Command
        /// </summary>
        private void ExecuteCloseWorkSpaceCommand()
        {
            CloseWorkSpaceCommand.CommandSucceeded = false;
 
            EventHandler<EventArgs> handlers = CloseWorkSpace;
 
            // Invoke the event handlers
            if (handlers != null)
            {
                try
                {
                    handlers(this, EventArgs.Empty);
                    CloseWorkSpaceCommand.CommandSucceeded = true;
                }
                catch
                {
                    Logger.Log(LogType.Error, "Error firing CloseWorkSpace event");
                }
            }
        }
 
        #endregion
 
        #region CloseWorkSpace Event
        /// <summary>
 
        /// Raised when this workspace should be removed from the UI.
        /// </summary>
        public event EventHandler<EventArgs> CloseWorkSpace;
        #endregion 
    }
}

To make use of this, simply inherit from this class. Now what we need to do is build up a collection of these WorkspaceViewModel ViewModels and bind them to a TabControl. Lets us see that next. Here is an example ViewModel that holds a collection of WorkspaceViewModel ViewModels and caters for them being added/removed:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
 
using Cinch;
using MVVM.Models;
 
namespace MVVM.ViewModels
{
    public class MainWindowViewModel : Cinch.ViewModelBase
    {
        private ObservableCollection<WorkspaceViewModel> workspaces;
 
        public MainWindowViewModel()
        {
            Workspaces = new ObservableCollection<WorkspaceViewModel>();
            Workspaces.CollectionChanged += this.OnWorkspacesChanged;
            StartPageViewModel startPageViewModel = new StartPageViewModel();
            startPageViewModel.IsCloseable = false;
            Workspaces.Add(startPageViewModel);
        }
 
        /// <summary>
        /// If we get a request to add a new Workspace, add a new WorkSpace to the 
        /// collection and hook up the CloseWorkSpace event in a weak manner
        /// </summary>
        private void OnWorkspacesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null && e.NewItems.Count != 0)
                foreach (WorkspaceViewModel workspace in e.NewItems)
                    workspace.CloseWorkSpace +=
                         new EventHandler<EventArgs>(OnCloseWorkSpace).
                             MakeWeak(eh => workspace.CloseWorkSpace -= eh);
        }
 
        /// <summary>
        /// If we get a request to close a new Workspace, remove the WorkSpace from the 
        /// collection
        /// </summary>
        private void OnCloseWorkSpace(object sender, EventArgs e)
        {
            WorkspaceViewModel workspace = sender as WorkspaceViewModel;
            workspace.Dispose();
            this.Workspaces.Remove(workspace);
        }

        /// <summary>
        /// Sets a ViewModel to be active, which for the View equates
        /// to selected Tab
        /// </summary>
        /// <param name="workspace">workspace to activate</param>
 
        private void SetActiveWorkspace(WorkspaceViewModel workspace)
        {
            ICollectionView collectionView = 
                CollectionViewSource.GetDefaultView(this.Workspaces);
 
            if (collectionView != null)
                collectionView.MoveCurrentTo(workspace);
        }
 
        /// <summary>
        /// The active workspace ViewModels
        /// </summary>
        public ObservableCollection<WorkspaceViewModel> Workspaces
        {
            get { return workspaces; }
            set
            {
                if (workspaces == null)
                {
                    workspaces = value;
                    OnPropertyChanged("Workspaces");
                }
            }
        }
    }
}

Now that we have a collection of WorkspaceViewModel ViewModels to bind to, we simply use an appropriate View control to represent them, such as a TabControl. Here is an example:

<local:TabControlEx x:Name="tabControl" 
        Grid.Row="2" Grid.Column="0" 
        IsSynchronizedWithCurrentItem="True" 
        ItemsSource="{Binding Path=Workspaces}" 
        RenderTransformOrigin="0.5,0.5"
        Template="{StaticResource MainTabControlTemplateEx}">
</local:TabControlEx>

Where the TabControlEx template looks like this:

<!-- MainTabControlTemplateEx -->
<ControlTemplate x:Key="MainTabControlTemplateEx"
                TargetType="{x:Type controls:TabControlEx}">
    <Grid>
        
        <Grid.RowDefinitions>
 
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="4"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <TabPanel x:Name="tabpanel" 
            Background="{StaticResource OutlookButtonHighlight}"
            Margin="0"
            Grid.Row="0"
            IsItemsHost="True" />
        
        <Grid Grid.Row="1" Background="Black" 
              HorizontalAlignment="Stretch"/>
 
              
        <Grid x:Name="PART_ItemsHolder"
              Grid.Row="2"/>
    </Grid>
    <!-- no content presenter -->
    <ControlTemplate.Triggers>
        <Trigger Property="TabStripPlacement" 
        Value="Top">
            <Setter TargetName="tabpanel" 
        Property="DockPanel.Dock" Value="Top"/>
 
            <Setter TargetName="PART_ItemsHolder" 
        Property="DockPanel.Dock" Value="Bottom"/>
        </Trigger>
        <Trigger Property="TabStripPlacement" 
        Value="Bottom">
            <Setter TargetName="tabpanel" 
        Property="DockPanel.Dock" Value="Bottom"/>
            <Setter TargetName="PART_ItemsHolder" 
        Property="DockPanel.Dock" Value="Top"/>
        </Trigger>
 
    </ControlTemplate.Triggers>
 
</ControlTemplate>

The very last piece in the puzzle is making sure the correct View is shown for the correct bound TabItem.Content (ViewModels that inherit from WorkspaceViewModel that are in the bound list). Basically, this is just a case of making sure that the correct View DataTemplates are available within the Resources section somewhere.

<DataTemplate DataType="{x:Type VM:StartPageViewModel}">
 
    <AdornerDecorator>
        <local:StartPageView />
    </AdornerDecorator>
</DataTemplate>
 
<DataTemplate DataType="{x:Type VM:AddEditCustomerViewModel}">
    <AdornerDecorator>
 
        <local:AddEditCustomerView />
    </AdornerDecorator>
</DataTemplate>
 
<DataTemplate DataType="{x:Type VM:SearchCustomersViewModel}">
    <AdornerDecorator>
        <local:SearchCustomersView />
 
    </AdornerDecorator>
</DataTemplate>

Setting Focus

Cinch contains a little helper class that can be used to set focus to a particular UI element, which in WPF is a lot harder than you think it should be. You need to ensure that there is a message pumped via the Dispatcher to really ensure setting focus on a control works. Anyway, here is the relevant Cinch code to do this:

/// <summary>
/// This class forces focus to set on the specified UIElement 
/// </summary>
public class FocusHelper
{
    /// <summary>
    /// Set focus to UIElement
    /// </summary>
    /// <param name="element">The element to set focus on</param>
 
    public static void Focus(UIElement element)
    {
        //Focus in a callback to run on another thread, ensuring the main 
        //UI thread is initialized by the time focus is set
        ThreadPool.QueueUserWorkItem(delegate(Object theElement)
        {
            UIElement elem = (UIElement)theElement;
            elem.Dispatcher.Invoke(DispatcherPriority.Normal,
                (Action)delegate()
                {
                    elem.Focus();
                    Keyboard.Focus(elem);
                });
        }, element);
    }
}

What's Coming Up?

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

  1. How to develop ViewModels using Cinch
  2. How to Unit test ViewModels using the Cinch app, including how to test background worker threads which may run within Cinch ViewModels
  3. 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

  • Initial issue.
  • 24/12/09: Replaced ILoggingService with Simple Logging Facade.
  • 07/05/10: Added text to show ILogger and also talks about how an IOC container can also be set in a ViewModel constructor.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here