Click here to Skip to main content
15,881,600 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="EventToCommand.cs" company="Catel development team">
//   Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
//   Class to binding any event to a command.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace Catel.MVVM.Commands
{
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// This class is based on the implementation of the <c>EventToCommand</c> class that can be found
    /// in the MVVM Light Toolkit (see http://mvvmlight.codeplex.com/).
    /// </remarks>
    public class EventToCommand : TriggerAction<FrameworkElement>
    {
        #region Variables
        #endregion

        #region Constructor & destructor
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether the <see cref="EventArgs"/> passed to the event handler
        /// should be passed to the command as well.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if the <see cref="EventArgs"/> passed to the event handler should be passed to the command; otherwise, <c>false</c>.
        /// </value>
        public bool PassEventArgsToCommand { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the associated object should be disabled when the command
        /// cannot be executed.
        /// </summary>
        /// <remarks>
        /// Wrapper for the DisableAssociatedObjectOnCannotExecute dependency property.
        /// </remarks>
        public bool DisableAssociatedObjectOnCannotExecute
        {
            get { return (bool)GetValue(DisableAssociatedObjectOnCannotExecuteProperty); }
            set { SetValue(DisableAssociatedObjectOnCannotExecuteProperty, value); }
        }

        /// <summary>
        /// DependencyProperty definition as the backing store for DisableAssociatedObjectOnCannotExecute.
        /// </summary>
        public static readonly DependencyProperty DisableAssociatedObjectOnCannotExecuteProperty =
            DependencyProperty.Register("DisableAssociatedObjectOnCannotExecute", typeof(bool), typeof(EventToCommand), new PropertyMetadata(true,
                (sender, e) => ((EventToCommand)sender).UpdateElementState()));
        
        /// <summary>
        /// Gets or sets the associated Command.
        /// </summary>
        /// <remarks>
        /// Wrapper for the Command dependency property.
        /// </remarks>
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        /// <summary>
        /// DependencyProperty definition as the backing store for Command.
        /// </summary>
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommand), new PropertyMetadata(null,
                (sender, e) => ((EventToCommand)sender).OnCommandChanged(sender, e)));

        /// <summary>
        /// Gets or sets the command parameter.
        /// </summary>
        /// <remarks>
        /// Wrapper for the CommandParameter dependency property.
        /// </remarks>
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        /// <summary>
        /// DependencyProperty definition as the backing store for CommandParameter.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(EventToCommand), new PropertyMetadata(null,
                (sender, e) => ((EventToCommand)sender).UpdateElementState()));
        #endregion

        #region Methods
        /// <summary>
        /// Called when the <see cref="Command"/> property has changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private void OnCommandChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue is ICommand)
            {
                ((ICommand)e.OldValue).CanExecuteChanged -= OnCommandCanExecuteChanged;
            }

            if (e.NewValue is ICommand)
            {
                ((ICommand)e.NewValue).CanExecuteChanged += OnCommandCanExecuteChanged;
            }

            UpdateElementState();
        }

        /// <summary>
        /// Called when the <c>CanExecute</c> state of a command has changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnCommandCanExecuteChanged(object sender, EventArgs e)
        {
            UpdateElementState();
        }

        /// <summary>
        /// Invokes the action without any parameter.
        /// </summary>
        public void Invoke()
        {
            Invoke(null);
        }

        /// <summary>
        /// Invokes the action.
        /// </summary>
        /// <param name="parameter">The parameter to the action. If the Action does not require a parameter, the parameter may be set to a null reference.</param>
        protected override void Invoke(object parameter)
        {
            if (IsAssociatedObjectDisabled() || (Command == null))
            {
                return;
            }

            object commandParameter = CommandParameter;
            if ((commandParameter == null) && PassEventArgsToCommand)
            {
                commandParameter = parameter;
            }

            if (Command.CanExecute(commandParameter))
            {
                Command.Execute(commandParameter);
            }
        }

        /// <summary>
        /// Checks whether the associated object is disabled or not.
        /// </summary>
        /// <returns><c>true</c> if the associated object is disabled; otherwise <c>false</c>.</returns>
        private bool IsAssociatedObjectDisabled()
        {
#if SILVERLIGHT
            return false;
#else
            return ((AssociatedObject != null) && !AssociatedObject.IsEnabled);
#endif
        }

        /// <summary>
        /// Updates the state of the associated element.
        /// </summary>
        private void UpdateElementState()
        {
            if ((AssociatedObject == null) || (Command == null))
            {
                return;
            }

#if !SILVERLIGHT
            AssociatedObject.IsEnabled = DisableAssociatedObjectOnCannotExecute ? Command.CanExecute(CommandParameter) : true;
            // TODO: Check if we can actually set it to true, or maybe we shouldn't do anything at all
#endif
        }

        /// <summary>
        /// Called when this trigger is attached to a <see cref="FrameworkElement"/>.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

            UpdateElementState();
        }
        #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