using System; using System.Collections.Generic; using System.Windows.Input; namespace Developpez.Dotnet.Windows.Input { /// /// Cette classe permet de déléguer la logique d'une commande à des méthodes passées en paramètre du constructeur, /// et permet à la vue de lier des commandes à des objets qui ne font pas partie de son arborescence. /// /// Cette classe est issue du MVVM Toolkit disponible à cette adresse : /// http://wpf.codeplex.com/Wiki/View.aspx?title=WPF%20Model-View-ViewModel%20Toolkit public class DelegateCommand : ICommand { #region Constructors /// /// Initialise une nouvelle instance de DelegateCommand, avec la méthode spécifiée pour Execute /// /// Méthode à exécuter quand la commande est invoquée public DelegateCommand(Action executeMethod) : this(executeMethod, null, false) { } /// /// Initialise une nouvelle instance de DelegateCommand, avec les méthodes spécifiées pour Execute et CanExecute /// /// Méthode à exécuter quand la commande est invoquée /// Méthode à exécuter pour vérifier si la commande peut être exécutée public DelegateCommand(Action executeMethod, Func canExecuteMethod) : this(executeMethod, canExecuteMethod, false) { } /// /// Initialise une nouvelle instance de DelegateCommand, avec les méthodes spécifiées pour Execute et CanExecute, en indiquant /// s'il faut désactiver le mécanisme de revérification automatique de la capacité d'exécution de la commande /// /// Méthode à exécuter quand la commande est invoquée /// Méthode à exécuter pour vérifier si la commande peut être exécutée /// true pour le mécanisme de revérification automatique de la capacité d'exécution de la commande public DelegateCommand(Action executeMethod, Func canExecuteMethod, bool isAutomaticRequeryDisabled) { if (executeMethod == null) { throw new ArgumentNullException("executeMethod"); } _executeMethod = executeMethod; _canExecuteMethod = canExecuteMethod; _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled; } #endregion #region Public Methods /// /// Détermine si la commande peut être exécutée /// /// true si la commande peut être exécutée, false sinon public bool CanExecute() { if (_canExecuteMethod != null) { return _canExecuteMethod(); } return true; } /// /// Exécute la commande /// public void Execute() { if (_executeMethod != null) { _executeMethod(); } } /// /// Active ou désactive le mécanisme de revérification automatique de la capacité d'exécution de la commande /// public bool IsAutomaticRequeryDisabled { get { return _isAutomaticRequeryDisabled; } set { if (_isAutomaticRequeryDisabled != value) { if (value) { CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers); } else { CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers); } _isAutomaticRequeryDisabled = value; } } } /// /// Déclenche l'évènement CanExecuteChanged /// public void RaiseCanExecuteChanged() { OnCanExecuteChanged(); } /// /// Méthode protégée virtuelle pour déclencher l'évènement CanExecuteChanged /// protected virtual void OnCanExecuteChanged() { CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers); } #endregion #region ICommand Members /// /// Implementation de ICommand.CanExecuteChanged /// public event EventHandler CanExecuteChanged { add { if (!_isAutomaticRequeryDisabled) { CommandManager.RequerySuggested += value; } CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2); } remove { if (!_isAutomaticRequeryDisabled) { CommandManager.RequerySuggested -= value; } CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value); } } /// /// Détermine si la commande peut être exécutée /// /// Ignoré /// true si la commande peut être exécutée, false sinon bool ICommand.CanExecute(object parameter) { return CanExecute(); } /// /// Exécute la commande /// /// Ignoré void ICommand.Execute(object parameter) { Execute(); } #endregion #region Data private readonly Action _executeMethod = null; private readonly Func _canExecuteMethod = null; private bool _isAutomaticRequeryDisabled = false; private List _canExecuteChangedHandlers; #endregion } /// /// Cette classe permet de déléguer la logique d'une commande à des méthodes passées en paramètre du constructeur, /// et permet à la vue de lier des commandes à des objets qui ne font pas partie de son arborescence. Cette version /// générique permet de spécifier le type du paramètre de la commande /// /// Cette classe est issue du MVVM Toolkit disponible à cette adresse : /// http://wpf.codeplex.com/Wiki/View.aspx?title=WPF%20Model-View-ViewModel%20Toolkit /// Type of the parameter passed to the delegates public class DelegateCommand : ICommand { #region Constructors /// /// Initialise une nouvelle instance de DelegateCommand<T>, avec la méthode spécifiée pour Execute /// /// Méthode à exécuter quand la commande est invoquée public DelegateCommand(Action executeMethod) : this(executeMethod, null, false) { } /// /// Initialise une nouvelle instance de DelegateCommand<T>, avec les méthodes spécifiées pour Execute et CanExecute /// /// Méthode à exécuter quand la commande est invoquée /// Méthode à exécuter pour vérifier si la commande peut être exécutée public DelegateCommand(Action executeMethod, Func canExecuteMethod) : this(executeMethod, canExecuteMethod, false) { } /// /// Initialise une nouvelle instance de DelegateCommand<T>, avec les méthodes spécifiées pour Execute et CanExecute, en indiquant /// s'il faut désactiver le mécanisme de revérification automatique de la capacité d'exécution de la commande /// /// Méthode à exécuter quand la commande est invoquée /// Méthode à exécuter pour vérifier si la commande peut être exécutée /// true pour le mécanisme de revérification automatique de la capacité d'exécution de la commande public DelegateCommand(Action executeMethod, Func canExecuteMethod, bool isAutomaticRequeryDisabled) { if (executeMethod == null) { throw new ArgumentNullException("executeMethod"); } _executeMethod = executeMethod; _canExecuteMethod = canExecuteMethod; _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled; } #endregion #region Public Methods /// /// Détermine si la commande peut être exécutée pour le paramètre spécifié /// /// Paramètre de la commande /// true si la commande peut être exécutée, false sinon public bool CanExecute(T parameter) { if (_canExecuteMethod != null) { return _canExecuteMethod(parameter); } return true; } /// /// Exécute la commande avec le paramètre spécifié /// /// Paramètre avec lequel exécuter la commande public void Execute(T parameter) { if (_executeMethod != null) { _executeMethod(parameter); } } /// /// Déclenche l'évènement CanExecuteChanged /// public void RaiseCanExecuteChanged() { OnCanExecuteChanged(); } /// /// Méthode protégée virtuelle pour déclencher l'évènement CanExecuteChanged /// protected virtual void OnCanExecuteChanged() { CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers); } /// /// Active ou désactive le mécanisme de revérification automatique de la capacité d'exécution de la commande /// public bool IsAutomaticRequeryDisabled { get { return _isAutomaticRequeryDisabled; } set { if (_isAutomaticRequeryDisabled != value) { if (value) { CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers); } else { CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers); } _isAutomaticRequeryDisabled = value; } } } #endregion #region ICommand Members /// /// Implementation de ICommand.CanExecuteChanged /// public event EventHandler CanExecuteChanged { add { if (!_isAutomaticRequeryDisabled) { CommandManager.RequerySuggested += value; } CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2); } remove { if (!_isAutomaticRequeryDisabled) { CommandManager.RequerySuggested -= value; } CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value); } } /// /// Détermine si la commande peut être exécutée pour le paramètre spécifié /// /// Paramètre de la commande /// true si la commande peut être exécutée, false sinon bool ICommand.CanExecute(object parameter) { // if T is of value type and the parameter is not // set yet, then return false if CanExecute delegate // exists, else return true if (parameter == null && typeof(T).IsValueType) { return (_canExecuteMethod == null); } return CanExecute((T)parameter); } /// /// Exécute la commande avec le paramètre spécifié /// /// Paramètre avec lequel exécuter la commande void ICommand.Execute(object parameter) { Execute((T)parameter); } #endregion #region Data private readonly Action _executeMethod = null; private readonly Func _canExecuteMethod = null; private bool _isAutomaticRequeryDisabled = false; private List _canExecuteChangedHandlers; #endregion } /// /// Cette classe contient des méthodes pour utiliser le CommandeManager en évitant les fuites mémoires grâce à /// l'utilisation de références faibles (WeakReference) /// internal class CommandManagerHelper { internal static void CallWeakReferenceHandlers(List handlers) { if (handlers != null) { // Take a snapshot of the handlers before we call out to them since the handlers // could cause the array to me modified while we are reading it. EventHandler[] callees = new EventHandler[handlers.Count]; int count = 0; for (int i = handlers.Count - 1; i >= 0; i--) { WeakReference reference = handlers[i]; EventHandler handler = reference.Target as EventHandler; if (handler == null) { // Clean up old handlers that have been collected handlers.RemoveAt(i); } else { callees[count] = handler; count++; } } // Call the handlers that we snapshotted for (int i = 0; i < count; i++) { EventHandler handler = callees[i]; handler(null, EventArgs.Empty); } } } internal static void AddHandlersToRequerySuggested(List handlers) { if (handlers != null) { foreach (WeakReference handlerRef in handlers) { EventHandler handler = handlerRef.Target as EventHandler; if (handler != null) { CommandManager.RequerySuggested += handler; } } } } internal static void RemoveHandlersFromRequerySuggested(List handlers) { if (handlers != null) { foreach (WeakReference handlerRef in handlers) { EventHandler handler = handlerRef.Target as EventHandler; if (handler != null) { CommandManager.RequerySuggested -= handler; } } } } internal static void AddWeakReferenceHandler(ref List handlers, EventHandler handler) { AddWeakReferenceHandler(ref handlers, handler, -1); } internal static void AddWeakReferenceHandler(ref List handlers, EventHandler handler, int defaultListSize) { if (handlers == null) { handlers = (defaultListSize > 0 ? new List(defaultListSize) : new List()); } handlers.Add(new WeakReference(handler)); } internal static void RemoveWeakReferenceHandler(List handlers, EventHandler handler) { if (handlers != null) { for (int i = handlers.Count - 1; i >= 0; i--) { WeakReference reference = handlers[i]; EventHandler existingHandler = reference.Target as EventHandler; if ((existingHandler == null) || (existingHandler == handler)) { // Clean up old handlers that have been collected // in addition to the handler that is to be removed. handlers.RemoveAt(i); } } } } } }