Désolé Dave mais je n'ai pas beaucoup aimé votre solution. Premièrement, vous devez coder le plomberie pour chaque commande manuellement dans le code, puis vous devez configurer le CommandRouter pour connaître chaque association view/viewmodel dans l'application.
J'ai pris une approche différente.
J'ai un assembly utilitaire Mvvm (qui n'a pas de dépendances WPF) et que j'utilise dans mon viewmodel. Dans cet assembly, je déclare une interface ICommand personnalisée et une classe DelegateCommand qui implémente cette interface.
namespace CommonUtil.Mvvm
{
using System;
public interface ICommand
{
void Execute(object parameter);
bool CanExecute(object parameter);
event EventHandler CanExecuteChanged;
}
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public DelegateCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_execute(parameter);
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
}
}
J'ai aussi un ensemble de bibliothèque WPF (qui fait référence aux bibliothèques système WPF), que je référence de mon projet UI WPF. Dans cet assembly, je déclare une classe CommandWrapper qui a l'interface standard System.Windows.Input.ICommand. CommandWrapper est construit en utilisant une instance de mon ICommand personnalisé et délègue simplement Execute, CanExecute et CanExecuteChanged directement à mon type ICommand personnalisé.
namespace WpfUtil
{
using System;
using System.Windows.Input;
public class CommandWrapper : ICommand
{
// Public.
public CommandWrapper(CommonUtil.Mvvm.ICommand source)
{
_source = source;
_source.CanExecuteChanged += OnSource_CanExecuteChanged;
CommandManager.RequerySuggested += OnCommandManager_RequerySuggested;
}
public void Execute(object parameter)
{
_source.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return _source.CanExecute(parameter);
}
public event System.EventHandler CanExecuteChanged = delegate { };
// Implementation.
private void OnSource_CanExecuteChanged(object sender, EventArgs args)
{
CanExecuteChanged(sender, args);
}
private void OnCommandManager_RequerySuggested(object sender, EventArgs args)
{
CanExecuteChanged(sender, args);
}
private readonly CommonUtil.Mvvm.ICommand _source;
}
}
Dans mon assemblage WPF je crée aussi un ValueConverter que lorsqu'il est passé une instance de mon habitude ICommand recrache une instance de la CommandWrapper compatible Windows.Input.ICommand.
namespace WpfUtil
{
using System;
using System.Globalization;
using System.Windows.Data;
public class CommandConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new CommandWrapper((CommonUtil.Mvvm.ICommand)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
}
Maintenant, mes viewmodels peut exposer les commandes comme des instances de mon type de commande personnalisée sans avoir à avoir une dépendance à l'égard WPF, et mon interface utilisateur peut lier Windows.Input.ICommand commandes à ces viewmodels utilisant mon ValueConverter comme si. (Spam XAML namespace spam).
<Window x:Class="Project1.MainWindow">
<Window.Resources>
<wpf:CommandConverter x:Key="_commandConv"/>
</Window.Resources>
<Grid>
<Button Content="Button1" Command="{Binding CustomCommandOnViewModel,
Converter={StaticResource _commandConv}}"/>
</Grid>
</Window>
Maintenant, si je suis vraiment paresseux (que je suis) et ne peut pas être pris la peine d'avoir à appliquer manuellement le CommandConverter chaque fois puis dans mon assemblée WPF je peux créer ma propre sous-classe Binding comme ceci:
namespace WpfUtil
{
using System.Windows.Data;
public class CommandBindingExtension : Binding
{
public CommandBindingExtension(string path) : base(path)
{
Converter = new CommandConverter();
}
}
}
alors maintenant, je peux lier à mon type de commande personnalisée encore plus simplement comme ceci:
<Window x:Class="Project1.MainWindow"
xmlns:wpf="clr-namespace:WpfUtil;assembly=WpfUtil">
<Window.Resources>
<wpf:CommandConverter x:Key="_commandConv"/>
</Window.Resources>
<Grid>
<Button Content="Button1" Command="{wpf:CommandBinding CustomCommandOnViewModel}"/>
</Grid>
</Window>
@aoven: Je suis là où vous étiez quand vous avez posé il y a 8 mois, et je me demandais ce que vous blessez en faisant et comment ça marche D pour toi. Salut – Berryl