C# 绑定到WinForms中的命令

C# 绑定到WinForms中的命令,c#,winforms,mvvm,C#,Winforms,Mvvm,如何将按钮绑定到视图模型(如WPF with MVVM)中的命令?我不认为您可以直接这样做,但如何使用按钮的单击处理程序来调用该命令?它不像WPF那样干净,但仍然可以实现分离。我建议实现它,您可以在WinForms和WPF中使用它。有关介绍和一些其他信息,请参见。我之前已将ICommand对象附加到按钮和菜单项的标记属性 然后,我只是看看我是否可以强制转换并运行它,如果可以的话,例如: private void button1_Click(object sender, EventArgs e)

如何将按钮绑定到视图模型(如WPF with MVVM)中的命令?

我不认为您可以直接这样做,但如何使用按钮的单击处理程序来调用该命令?它不像WPF那样干净,但仍然可以实现分离。

我建议实现它,您可以在WinForms和WPF中使用它。有关介绍和一些其他信息,请参见。

我之前已将
ICommand
对象附加到
按钮和
菜单项的
标记
属性

然后,我只是看看我是否可以强制转换并运行它,如果可以的话,例如:

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

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

为了更简单,可以尝试将控件子类化(例如
按钮
菜单项

您可能会发现很有趣。它展示了如何在Windows窗体应用程序中应用模型-视图-模型(MVVM)模式。适配器实现为Windows窗体中缺少的命令支持提供了解决方案。

我想知道是否可以做同样的事情,并结束了编写一个简单的CommandManager来查询注册的命令(在Application.Idle事件上)并使用数据绑定来更改控件的启用状态

这是我现在正在使用的代码:

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

另外,如果您需要该代码,我在博客中介绍了它

您可以创建一个通用命令绑定类,该类允许将命令绑定到从
按钮库
继承的任何类

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); };
    }
}
公共类CommandBinding,其中T:ButtonBase
{
私有T_调用程序;
专用ICommand_命令;
公共命令绑定(T调用程序、ICommand命令)
{
_调用方=调用方;
_命令=命令;
_Enabled=_command.CanExecute(null);
_Click+=delegate{u command.Execute(null);};
_command.CanExecuteChanged+=委托{{u invoker.Enabled=\u command.CanExecute(null);};
}
}
然后可以使用以下代码设置命令绑定:

CommandBinding<Button> cmdBinding = 
    new CommandBinding<Button>(btnCut, CutCommand);
CommandBinding cmdBinding=
新命令绑定(btnCut、CUTCOMAND);
这只是我的实现的基本内容,让您有一个开始,因此自然有几个注意事项:

  • 该示例假定使用WPF
    ICommand
    接口,因此如果您有自己的命令模式实现,则可能必须对其进行更改
  • 应检查传入的参数是否存在空引用
  • 更具体的实现应该有一些删除事件处理程序的方法,以避免内存泄漏

通用约束也可以更改为
控件
,它公开
单击
事件和
启用
属性,这意味着命令几乎可以绑定到任何控件

如果要使用设计器将命令绑定到控件,请查看此演示应用程序,我将在其中演示如何在Windows窗体中使用MVVM:

button1.Click += (s, e) => new MyCommand().Execute();


您必须在代码隐藏中编写的唯一代码是创建视图模型实例。

我的方法基于Gale的答案,但使用扩展方法并使绑定可用于Lifecycle管理:

公共静态类按钮
{
公共静态IDisposable绑定(此ButtonBase调用程序,ICommand命令)
{
无效单击(对象发送方,事件args args)=>command.Execute(null);
void CanExecuteChanged(对象发送方,EventArgs args)=>invoker.Enabled=command.CanExecute(null);
invoker.Enabled=command.CanExecute(null);
调用程序。单击+=单击;
command.CanExecuteChanged+=CanExecuteChanged;
返回一次性。创建(()=>
{
invoker.Enabled=false;
调用程序。单击-=单击;
command.CanExecuteChanged-=CanExecuteChanged;
});
}
}
您可以这样使用它:

private List Disposables{get;}=new List();
私有ICommand MyCommand{get;}
公共MyControl()
{
MyCommand=DelegateCommand.Create(()=>…);
一次性使用。添加(myButton.Bind(MyCommand));
}
~MyControl()
{
foreach(一次性使用的var)
{
一次性的?.Dispose();
}
}
但是,您可能更喜欢使用ReactiveUI,它本机支持:


是的,事实上这就是我现在正在做的事情-我只是想知道是否有一种合理的方法来使用绑定。您的解决方案工作得非常好,并且使我的对话框组件更容易理解:-)。谢谢!你能解释一下为什么在
DataBindings.Add(…)
方法中使用
Application.Idle
事件而不是使用
DataSourceUpdateMode
吗?还有一篇Josh Smith的文章介绍了WinForms内置CommandManager的更多细节:@Fabian这篇文章似乎是针对WPF的。文本中没有提到Winforms。@dsanchez您是对的,这是WPF。很抱歉,我已经有一段时间没有发布这个了。我想我的意思是,可以使用.Net框架提供的CommandManager。本文展示了ViewModel部分。
button1.Click += (s, e) => new MyCommand().Execute();