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