Liaison aux commandes dans WinForms

comment un bouton peut-il être relié à une commande dans un modèle de vue comme dans WPF avec MVVM?

17
demandé sur Mark Bostleman 2009-11-06 05:44:14

8 réponses

j'ai joint ICommand objets TagButton et MenuItem objets avant.

Alors, je viens voir si je peux en fonte et de l'exécuter si je peux, par exemple:

private void button1_Click(object sender, EventArgs e)
{
    ICommand command = ((Control)(sender)).Tag as ICommand;

    if (command != null)
    {
        command.Execute();
    }
}

pour une vie encore plus facile, essayez de sous-classer les contrôles (par exemple Button,MenuItem)

3
répondu Brett Veenstra 2012-10-14 19:31:14

je me demandais si la même chose pouvait être faite et j'ai fini par écrire un simple CommandManager qui interroge les commandes enregistrées (sur l'Application.Idle event) et utilise la databinding pour changer l'état Activé du contrôle

voici le code que j'utilise en ce moment:

public class CommandManager: Component
{
    private IList<ICommand> Commands { get; set; }
    private IList<ICommandBinder> Binders { get; set; }

    public CommandManager()
    {
        Commands = new List<ICommand>();

        Binders = new List<ICommandBinder>
                      {
                          new ControlBinder(),
                          new MenuItemCommandBinder()
                      };

        Application.Idle += UpdateCommandState;
    }

    private void UpdateCommandState(object sender, EventArgs e)
    {
        Commands.Do(c => c.Enabled);
    }

    public CommandManager Bind(ICommand command, IComponent component)
    {
        if (!Commands.Contains(command))
            Commands.Add(command);

        FindBinder(component).Bind(command, component);
        return this;
    }

    protected ICommandBinder FindBinder(IComponent component)
    {
        var binder = GetBinderFor(component);

        if (binder == null)
            throw new Exception(string.Format("No binding found for component of type {0}", component.GetType().Name));

        return binder;
    }

    private ICommandBinder GetBinderFor(IComponent component)
    {
        var type = component.GetType();
        while (type != null)
        {
            var binder = Binders.FirstOrDefault(x => x.SourceType == type);
            if (binder != null)
                return binder;

            type = type.BaseType;
        }

        return null;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            Application.Idle -= UpdateCommandState;

        base.Dispose(disposing);
    }
}

public static class Extensions
{
    public static void Do<T>(this IEnumerable<T> @this, Func<T, object> lambda)
    {
        foreach (var item in @this)
            lambda(item);
    }
}
public abstract class CommandBinder<T> : ICommandBinder where T: IComponent
{
    public Type SourceType
    {
        get { return typeof (T); }
    }

    public void Bind(ICommand command, object source)
    {
        Bind(command, (T) source); 
    }

    protected abstract void Bind(ICommand command, T source);
}

public class ControlBinder: CommandBinder<Control>
{
    protected override void Bind(ICommand command, Control source)
    {
        source.DataBindings.Add("Enabled", command, "Enabled");
        source.DataBindings.Add("Text", command, "Name");
        source.Click += (o, e) => command.Execute();
    }
}

public class MenuItemCommandBinder : CommandBinder<ToolStripItem>
{
    protected override void Bind(ICommand command, ToolStripItem source)
    {
        source.Text = command.Name;
        source.Enabled = command.Enabled;
        source.Click += (o, e) => command.Execute();

        command.PropertyChanged += (o, e) => source.Enabled = command.Enabled;
    }
}

et c'est un exemple de comment l'utiliser:

public partial class Form1 : Form
{
    private CommandManager commandManager;

    public ICommand CommandA { get; set; }
    public ICommand CommandB { get; set; }

    public bool condition;

    public Form1()
    {
        InitializeComponent();

        commandManager = new CommandManager();

        CommandA = new DelegateCommand("Command 1", OnTrue, OnExecute);
        CommandB = new DelegateCommand("Command 2", OnFalse, OnExecute);

        commandManager.Bind(CommandA, button1);
        commandManager.Bind(CommandB, button2);

        commandManager.Bind(CommandA, command1ToolStripMenuItem);
        commandManager.Bind(CommandB, command2ToolStripMenuItem);
    }

    private bool OnFalse()
    {
        return !condition;
    }

    private bool OnTrue()
    {
        return condition;
    }

    private void OnExecute()
    {
        condition = !condition;
    }
}

Aussi, si vous avez besoin du code, j'ai blogué à ce sujet ici

15
répondu Sebastian Piu 2010-11-20 11:50:49

vous pouvez créer une classe de liaison de commandes générique qui permet à une commande d'être liée à n'importe quelle classe qui hérite de ButtonBase.

public class CommandBinding<T> where T : ButtonBase
{
    private T _invoker;
    private ICommand _command;

    public CommandBinding(T invoker, ICommand command)
    {
        _invoker = invoker;
        _command = command;

        _invoker.Enabled = _command.CanExecute(null);
        _invoker.Click += delegate { _command.Execute(null); };
        _command.CanExecuteChanged += delegate { _invoker.Enabled = _command.CanExecute(null); };
    }
}

la liaison de commande peut alors être établie en utilisant le code suivant:

CommandBinding<Button> cmdBinding = 
    new CommandBinding<Button>(btnCut, CutCommand);

ce ne sont que les os de mon implémentation pour vous donner un début donc naturellement il y a quelques mises en garde:

  • l'exemple suppose l'utilisation du WPF ICommand interface peut être modifié, si vous avez votre propre mise en œuvre du schéma de commandement.
  • les paramètres qui sont passés doivent être vérifiés pour les références nulles.
  • une implémentation plus concrète devrait avoir une méthode pour enlever les gestionnaires d'événements afin d'éviter les fuites de mémoire.

la contrainte générique peut aussi être changée en Control ce qui expose le Click l'événement et le Enabled propriété qui signifie que les commandes peuvent être liés à presque n'importe quel Contrôle.

5
répondu Benjamin Gale 2012-09-23 17:43:54
button1.Click += (s, e) => new MyCommand().Execute();
1
répondu Olivier Jacot-Descombes 2012-09-23 18:03:09

si vous voulez lier la commande au contrôle en utilisant le designer, Vérifiez cette application de démonstration où je montre comment utiliser MVVM dans les formes Windows:

https://bitbucket.org/lbras/mvvmforms

le seul code que vous devez écrire dans le code-behind est la création de l'instance view model.

1
répondu lbras 2013-03-11 13:15:05

Je ne pense pas que vous pouvez le faire directement, mais que diriez-vous d'utiliser le gestionnaire de clic du bouton pour invoquer la commande? Ce n'est pas aussi propre que la WPF, mais vous avez quand même votre séparation.

0
répondu serialhobbyist 2009-11-06 08:18:51

je recommande la mise en œuvre de INotifyPropertyChanged vous pouvez l'utiliser dans WinForms aussi bien que WPF. Voir ici pour une introduction et ici quelques informations supplémentaires.

0
répondu Matt Warren 2009-11-09 23:12:23

Vous pouvez trouver le WAF Windows Forms Adaptateur intéressant. Il montre comment appliquer le modèle Model-View-ViewModel (MVVM) dans une application Windows Forms. L'implémentation de L'adaptateur fournit une solution pour les Commande support dans les formes Windows.

0
répondu jbe 2010-09-20 18:20:17