Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / WPF

Catel - Part 4 of n: Unit testing with Catel

Rate me:
Please Sign up or sign in to vote.
4.55/5 (10 votes)
28 Jan 2011CPOL11 min read 48.8K   572   11  
This article explains how to write unit tests for MVVM using Catel.
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Command.cs" company="Catel development team">
//   Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
//   Class to implement commands in the <see cref="ViewModelBase" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Windows.Input;

namespace Catel.MVVM
{
    /// <summary>
    /// Class to implement commands in the <see cref="ViewModelBase"/>.
    /// </summary>
    /// <typeparam name="TExecuteParameter">The type of the execute parameter.</typeparam>
    /// <typeparam name="TCanExecuteParameter">The type of the can execute parameter.</typeparam>	
    /// <remarks>
    /// This is partly based on the Command as implemented in Cinch. Cinch is another open-source MVVM-framework
    /// (see http://cinch.codeplex.com), which (probably) based the command on http://marlongrech.wordpress.com/2008/11/26/avoiding-commandbinding-in-the-xaml-code-behind-files/.
    /// </remarks>
    public class Command<TExecuteParameter, TCanExecuteParameter> : ICommand, ICatelCommand
    {
        #region Variables
        private readonly Func<TCanExecuteParameter, bool> _canExecute;
        private readonly Action<TExecuteParameter> _execute;
        #endregion

        #region Constructor & destructor
        /// <summary>
        /// Initializes a new instance of the <see cref="Command{TCanExecuteParameter,TExecuteParameter}"/> class.
        /// </summary>
        /// <param name="execute">The action to execute.</param>
        public Command(Action<TExecuteParameter> execute)
            : this(execute, null) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Command{TCanExecuteParameter,TExecuteParameter}"/> class.
        /// </summary>
        /// <param name="canExecute">The function to call to determine wether the command can be executed.</param>
        /// <param name="execute">The action to execute.</param>
        public Command(Action<TExecuteParameter> execute, Func<TCanExecuteParameter, bool> canExecute)
        {
            _canExecute = canExecute;
            _execute = execute;
        }
        #endregion

        #region Events
        /// <summary>
        /// Occurs when changes occur that affect whether or not the command should execute.
        /// </summary>
#if SILVERLIGHT
        public event EventHandler CanExecuteChanged;
#else
        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null) 
                {
                    CommandManager.RequerySuggested += value;
                }
            }

            remove
            {
                if (_canExecute != null)
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
        }
#endif
        #endregion

        #region Properties
        #endregion

        #region Methods
        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        /// <returns>
        /// true if this command can be executed; otherwise, false.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            return CanExecute((TCanExecuteParameter)parameter);
        }

        /// <summary>
        /// Determines whether this instance can execute the specified parameter.
        /// </summary>
        /// <param name="parameter">The parameter.</param>
        /// <returns>
        /// 	<c>true</c> if this instance can execute the specified parameter; otherwise, <c>false</c>.
        /// </returns>
        public bool CanExecute(TCanExecuteParameter parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }

            return _canExecute(parameter);
        }

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        public void Execute(object parameter)
        {
            Execute((TExecuteParameter)parameter);
        }

        /// <summary>
        /// Defines the method to be called when the command is invoked.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        public void Execute(TExecuteParameter parameter)
        {
            if (_execute != null)
            {
                _execute(parameter);
            }
        }

        /// <summary>
        /// Raises the <see cref="CanExecuteChanged"/> event.
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
#if SILVERLIGHT
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
#else
            CommandManager.InvalidateRequerySuggested();
#endif
        }
        #endregion
    }

    /// <summary>
    /// Implements the <see cref="Command{TExecuteParameter, TCanExecuteParameter}"/> class with only the <typeparamref name="TExecuteParameter"/> as generic type.
    /// </summary>
    /// <typeparam name="TExecuteParameter">The type of the execute parameter.</typeparam>
    public class Command<TExecuteParameter> : Command<TExecuteParameter, object>
    {
        #region Constructor & destructor
        /// <summary>
        /// Initializes a new instance of the <see cref="Command{TCanExecuteParameter,TExecuteParameter}"/> class.
        /// </summary>
        /// <param name="execute">The action to execute.</param>
        public Command(Action<TExecuteParameter> execute)
            : this(execute, null) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Command{TCanExecuteParameter,TExecuteParameter}"/> class.
        /// </summary>
        /// <param name="canExecute">The function to call to determine wether the command can be executed.</param>
        /// <param name="execute">The action to execute.</param>
        public Command(Action<TExecuteParameter> execute, Func<object, bool> canExecute)
            : base(execute, canExecute) { }
        #endregion
    }

    /// <summary>
    /// Implements the <see cref="Command{TExecuteParameter, TCanExecuteParameter}"/> class with <see cref="Object"/> as generic types.
    /// </summary>
    public class Command : Command<object, object>
    {
        #region Constructor & destructor
        /// <summary>
        /// Initializes a new instance of the <see cref="Command{TCanExecuteParameter,TExecuteParameter}"/> class.
        /// </summary>
        /// <param name="execute">The action to execute.</param>
        public Command(Action<object> execute)
            : this(execute, null) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="Command{TCanExecuteParameter,TExecuteParameter}"/> class.
        /// </summary>
        /// <param name="canExecute">The function to call to determine wether the command can be executed.</param>
        /// <param name="execute">The action to execute.</param>
        public Command(Action<object> execute, Func<object, bool> canExecute)
            : base(execute, canExecute) { }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Software Developer
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions