// THIS SOFTWARE COMES "AS IS", WITH NO WARRANTIES. THIS
// MEANS NO EXPRESS, IMPLIED OR STATUTORY WARRANTY, INCLUDING
// WITHOUT LIMITATION, WARRANTIES OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE OR ANY WARRANTY OF TITLE OR
// NON-INFRINGEMENT.
//
// MICROSOFT WILL NOT BE LIABLE FOR ANY DAMAGES RELATED TO
// THE SOFTWARE, INCLUDING DIRECT, INDIRECT, SPECIAL,
// CONSEQUENTIAL OR INCIDENTAL DAMAGES, TO THE MAXIMUM EXTENT
// THE LAW PERMITS, NO MATTER WHAT LEGAL THEORY IT IS
// BASED ON.
using System;
using System.Windows.Input;
using System.Collections.Generic;
using System.ComponentModel;
namespace PersistingWorkflows
{
///
/// A map that exposes commands in a WPF binding friendly manner
///
[TypeDescriptionProvider(typeof(CommandMapDescriptionProvider))]
public class CommandMap
{
///
/// Add a named command to the command map
///
/// The name of the command
/// The method to execute
public void AddCommand(string commandName, Action executeMethod)
{
Commands[commandName] = new DelegateCommand(executeMethod);
}
///
/// Add a named command to the command map
///
/// The name of the command
/// The method to execute
/// The method to execute to check if the command can be executed
public void AddCommand(string commandName, Action executeMethod, Predicate canExecuteMethod)
{
Commands[commandName] = new DelegateCommand(executeMethod, canExecuteMethod);
}
///
/// Remove a command from the command map
///
/// The name of the command
public void RemoveCommand(string commandName)
{
Commands.Remove(commandName);
}
///
/// Expose the dictionary of commands
///
protected Dictionary Commands
{
get
{
if (null == _commands)
_commands = new Dictionary();
return _commands;
}
}
///
/// Store the commands
///
private Dictionary _commands;
///
/// Implements ICommand in a delegate friendly way
///
private class DelegateCommand : ICommand
{
///
/// Create a command that can always be executed
///
/// The method to execute when the command is called
public DelegateCommand(Action executeMethod) : this(executeMethod, null) { }
///
/// Create a delegate command which executes the canExecuteMethod before executing the executeMethod
///
///
///
public DelegateCommand(Action executeMethod, Predicate canExecuteMethod)
{
if (null == executeMethod)
throw new ArgumentNullException("executeMethod");
this._executeMethod = executeMethod;
this._canExecuteMethod = canExecuteMethod;
}
public bool CanExecute(object parameter)
{
return (null == _canExecuteMethod) ? true : _canExecuteMethod(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_executeMethod(parameter);
}
private Predicate _canExecuteMethod;
private Action _executeMethod;
}
///
/// Expose the dictionary entries of a CommandMap as properties
///
private class CommandMapDescriptionProvider : TypeDescriptionProvider
{
///
/// Standard constructor
///
public CommandMapDescriptionProvider()
: this(TypeDescriptor.GetProvider(typeof(CommandMap)))
{
}
///
/// Construct the provider based on a parent provider
///
///
public CommandMapDescriptionProvider(TypeDescriptionProvider parent)
: base(parent)
{
}
///
/// Get the type descriptor for a given object instance
///
/// The type of object for which a type descriptor is requested
/// The instance of the object
/// A custom type descriptor
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
{
return new CommandMapDescriptor(base.GetTypeDescriptor(objectType, instance), instance as CommandMap);
}
}
///
/// This class is responsible for providing custom properties to WPF - in this instance
/// allowing you to bind to commands by name
///
private class CommandMapDescriptor : CustomTypeDescriptor
{
///
/// Store the command map for later
///
///
///
public CommandMapDescriptor(ICustomTypeDescriptor descriptor, CommandMap map)
: base(descriptor)
{
_map = map;
}
///
/// Get the properties for this command map
///
/// A collection of synthesized property descriptors
public override PropertyDescriptorCollection GetProperties()
{
//TODO: See about caching these properties (need the _map to be observable so can respond to add/remove)
PropertyDescriptor[] props = new PropertyDescriptor[_map.Commands.Count];
int pos = 0;
foreach (KeyValuePair command in _map.Commands)
props[pos++] = new CommandPropertyDescriptor(command);
return new PropertyDescriptorCollection(props);
}
private CommandMap _map;
}
///
/// A property descriptor which exposes an ICommand instance
///
private class CommandPropertyDescriptor : PropertyDescriptor
{
///
/// Construct the descriptor
///
///
public CommandPropertyDescriptor(KeyValuePair command)
: base(command.Key, null)
{
_command = command.Value;
}
///
/// Always read only in this case
///
public override bool IsReadOnly
{
get { return true; }
}
///
/// Nope, it's read only
///
///
///
public override bool CanResetValue(object component)
{
return false;
}
///
/// Not needed
///
public override Type ComponentType
{
get { throw new NotImplementedException(); }
}
///
/// Get the ICommand from the parent command map
///
///
///
public override object GetValue(object component)
{
CommandMap map = component as CommandMap;
if (null == map)
throw new ArgumentException("component is not a CommandMap instance", "component");
return map.Commands[this.Name];
}
///
/// Get the type of the property
///
public override Type PropertyType
{
get { return typeof(ICommand); }
}
///
/// Not needed
///
///
public override void ResetValue(object component)
{
throw new NotImplementedException();
}
///
/// Not needed
///
///
///
public override void SetValue(object component, object value)
{
throw new NotImplementedException();
}
///
/// Not needed
///
///
///
public override bool ShouldSerializeValue(object component)
{
return false;
}
///
/// Store the command which will be executed
///
private ICommand _command;
}
}
}