// 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; } } }