using System; using System.Reflection; using System.Windows; using System.Windows.Input; using System.Windows.Markup; namespace Developpez.Dotnet.Windows.Input { /// /// Permet de binder la commande d'un InputBinding sur une commande du DataContext /// /// Le binding n'est possible que sur le DataContext de la racine du document XAML [MarkupExtensionReturnType(typeof(ICommand))] public class CommandBindingExtension : MarkupExtension { /// /// Crée une instance de CommandBindingExtension /// public CommandBindingExtension() { } /// /// Crée une instance de CommandBindingExtension avec le nom de commande spécifié /// /// Nom de la commande dans le DataContext public CommandBindingExtension(string commandName) { this.CommandName = commandName; } /// /// Nom de la commande dans le DataContext /// [ConstructorArgument("commandName")] public string CommandName { get; set; } /// /// Objet cible de la markup extension /// protected object TargetObject { get; set; } /// /// Propriété cible de la markup extension /// protected object TargetProperty { get; set; } /// /// Renvoie la commande à affecter à la propriété cible /// /// Objet qui peut fournir des services pour la markup extension /// La commande à affecter à la propriété cible public override object ProvideValue(IServiceProvider serviceProvider) { IProvideValueTarget provideValueTarget = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; if (provideValueTarget != null) { TargetObject = provideValueTarget.TargetObject; TargetProperty = provideValueTarget.TargetProperty; } if (!string.IsNullOrEmpty(CommandName)) { ParserContext parserContext = GetPrivateFieldValue(serviceProvider, "_context"); if (parserContext != null) { FrameworkElement rootElement = GetPrivateFieldValue(parserContext, "_rootElement"); if (rootElement != null) { object dataContext = rootElement.DataContext; if (dataContext != null) { ICommand command = GetCommand(dataContext, CommandName); if (command != null) return command; } else { if (!dataContextChangeHandlerSet) { rootElement.DataContextChanged += new DependencyPropertyChangedEventHandler(rootElement_DataContextChanged); dataContextChangeHandlerSet = true; } } } } } return DummyCommand.Instance; } /// /// Renvoie la commande spécifiée de l'objet spécifié. /// /// L'objet dont on veut récupérer une commande /// Le nom de la commande /// L'objet ICommand demandé, si la propriété existe /// et est une commande, null dans les autres cas private ICommand GetCommand(object dataContext, string commandName) { PropertyInfo prop = dataContext.GetType().GetProperty(commandName); if (prop != null) { ICommand command = prop.GetValue(dataContext, null) as ICommand; return command; } return null; } /// /// Affecte la commande à la propriété cible /// /// La commande à affecter private void AssignCommand(ICommand command) { if (TargetObject != null && TargetProperty != null) { if (TargetProperty is DependencyProperty) { DependencyObject depObj = TargetObject as DependencyObject; DependencyProperty depProp = TargetProperty as DependencyProperty; depObj.SetValue(depProp, command); } else { PropertyInfo prop = TargetProperty as PropertyInfo; prop.SetValue(TargetObject, command, null); } } } private bool dataContextChangeHandlerSet = false; /// /// En cas de changement du DataContext, actualise la propriété cible avec la commande /// du nouveau DataContext /// private void rootElement_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) { FrameworkElement rootElement = sender as FrameworkElement; if (rootElement != null) { object dataContext = rootElement.DataContext; if (dataContext != null) { ICommand command = GetCommand(dataContext, CommandName); if (command != null) { AssignCommand(command); } } } } /// /// Renvoie la valeur d'un champ non public d'un objet /// /// Type du champ à obtenir /// Objet dont on veut obtenir la valeur du champ /// Nom du champ /// La valeur de ce champ s'il existe, ou la valeur par défaut du type spécifié si le champ n'existe pas private T GetPrivateFieldValue(object target, string fieldName) { FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { return (T)field.GetValue(target); } return default(T); } /// /// Commande "bouchon" qui ne fait rien, utilisée comme commande par défaut /// private class DummyCommand : ICommand { #region Pattern Singleton /// /// Constructeur privé /// private DummyCommand() { } private static DummyCommand _instance = null; /// /// Renvoie l'instance unique de la classe /// public static DummyCommand Instance { get { if (_instance == null) { _instance = new DummyCommand(); } return _instance; } } #endregion // Pattern Singleton #region Membres de ICommand /// /// Indique si la commande peut être exécutée /// /// Paramètre de la commande /// Renvoie toujours false public bool CanExecute(object parameter) { return false; } /// /// Se produit lorsque des modifications influent sur l'exécution de la commande /// Inutilisé, présent uniquement pour se conformer à l'interface /// event EventHandler ICommand.CanExecuteChanged { add { } remove { } } /// /// Ne fait rien /// /// Paramètre de la commande public void Execute(object parameter) { } #endregion // Membres de ICommand } } }