C# ICommand MVVM 实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1468791/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 17:51:00  来源:igfitidea点击:

ICommand MVVM implementation

c#.netwpfmvvmicommand

提问by Carlo

So in this particular MVVM implementation I'm doing, I need several commands. I really got tired of implementing the ICommand classes one by one, so I came up with a solution, but I don't know how good it is, so the input of any WPF expert here will be greatly appreciated. And if you could provide a better solution, even better.

所以在我正在做的这个特定的 MVVM 实现中,我需要几个命令。我真的厌倦了一个一个地实现 ICommand 类,所以我想出了一个解决方案,但我不知道它有多好,所以在这里任何 WPF 专家的输入将不胜感激。如果你能提供更好的解决方案,那就更好了。

What I did is a single ICommand class and two delegates which take an object as a parameter, one delegate is void (for OnExecute), the other bool (for OnCanExecute). So in the constructor of my ICommand (which is called by the ViewModel class) I send the two methods, and on each ICommand method I invoke the delegates' methods.

我所做的是一个 ICommand 类和两个将对象作为参数的委托,一个委托是 void(对于 OnExecute),另一个是 bool(对于 OnCanExecute)。因此,在 ICommand 的构造函数(由 ViewModel 类调用)中,我发送了两个方法,并在每个 ICommand 方法上调用了委托的方法。

It works really good, but I'm not sure if this is a bad way to do it, or if there's a better way. Below is the complete code, any input will be greatly appreciated, even negative, but please be constructive.

它真的很好用,但我不确定这是否是一种糟糕的方法,或者是否有更好的方法。下面是完整的代码,任何输入将不胜感激,即使是负面的,但请建设性。

ViewModel:

视图模型:

public class TestViewModel : DependencyObject
{
    public ICommand Command1 { get; set; }
    public ICommand Command2 { get; set; }
    public ICommand Command3 { get; set; }

    public TestViewModel()
    {
        this.Command1 = new TestCommand(ExecuteCommand1, CanExecuteCommand1);
        this.Command2 = new TestCommand(ExecuteCommand2, CanExecuteCommand2);
        this.Command3 = new TestCommand(ExecuteCommand3, CanExecuteCommand3);
    }

    public bool CanExecuteCommand1(object parameter)
    {
        return true;
    }

    public void ExecuteCommand1(object parameter)
    {
        MessageBox.Show("Executing command 1");
    }

    public bool CanExecuteCommand2(object parameter)
    {
        return true;
    }

    public void ExecuteCommand2(object parameter)
    {
        MessageBox.Show("Executing command 2");
    }

    public bool CanExecuteCommand3(object parameter)
    {
        return true;
    }

    public void ExecuteCommand3(object parameter)
    {
        MessageBox.Show("Executing command 3");
    }
}

ICommand:

指令:

public class TestCommand : ICommand
{
    public delegate void ICommandOnExecute(object parameter);
    public delegate bool ICommandOnCanExecute(object parameter);

    private ICommandOnExecute _execute;
    private ICommandOnCanExecute _canExecute;

    public TestCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
    {
        _execute = onExecuteMethod;
        _canExecute = onCanExecuteMethod;
    }

    #region ICommand Members

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute.Invoke(parameter);
    }

    public void Execute(object parameter)
    {
        _execute.Invoke(parameter);
    }

    #endregion
}

采纳答案by Jarrett Meyer

This is almost identical to how Karl Shifflet demonstrateda RelayCommand, where Executefires a predetermined Action<T>. A top-notch solution, if you ask me.

这几乎与Karl Shifflet 演示的相同RelayCommand,其中Execute发射预定Action<T>。如果你问我,这是一个一流的解决方案。

public class RelayCommand : ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

    public RelayCommand(Predicate<object> canExecute, Action<object> execute)
    {
        _canExecute = canExecute;
        _execute = execute;
    }

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

This could then be used as...

然后这可以用作...

public class MyViewModel
{
    private ICommand _doSomething;
    public ICommand DoSomethingCommand
    {
        get
        {
            if (_doSomething == null)
            {
                _doSomething = new RelayCommand(
                    p => this.CanDoSomething,
                    p => this.DoSomeImportantMethod());
            }
            return _doSomething;
        }
    }
}

Read more:
Josh Smith (introducer of RelayCommand): Patterns - WPF Apps With The MVVM Design Pattern

阅读更多:
Josh Smith(介绍人RelayCommand):模式 - 使用 MVVM 设计模式的 WPF 应用程序

回答by Boris Treukhov

I've just created a little exampleshowing how to implement commands in convention over configuration style. However it requires Reflection.Emit() to be available. The supporting code may seem a little weird but once written it can be used many times.

我刚刚创建了一个小示例,展示了如何以约定方式实现命令而不是配置样式。但是它需要 Reflection.Emit() 可用。支持代码可能看起来有点奇怪,但是一旦编写它可以多次使用。

Teaser:

预告片:

public class SampleViewModel: BaseViewModelStub
{
    public string Name { get; set; }

    [UiCommand]
    public void HelloWorld()
    {
        MessageBox.Show("Hello World!");
    }

    [UiCommand]
    public void Print()
    {
        MessageBox.Show(String.Concat("Hello, ", Name, "!"), "SampleViewModel");
    }

    public bool CanPrint()
    {
        return !String.IsNullOrEmpty(Name);
    }
}

}

}

UPDATE: now there seem to exist some libraries like http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Modelthat solve the problem of ICommand boilerplate code.

更新:现在似乎存在一些库,如http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model解决了 ICommand 样板代码的问题。

回答by kurik

I have written this articleabout the ICommand interface.

我写了这篇关于 ICommand 接口的文章

The idea - creating a universal command that takes two delegates: one is called when ICommand.Execute (object param)is invoked, the second checks the status of whether you can execute the command (ICommand.CanExecute (object param)).

想法 - 创建一个需要两个委托的通用命令:一个在ICommand.Execute (object param)被调用时调用,第二个检查是否可以执行命令的状态(ICommand.CanExecute (object param))

Requires the method to switching event CanExecuteChanged. It is called from the user interface elements for switching the state CanExecute()command.

需要切换事件的方法CanExecuteChanged。它是从用户界面元素调用的,用于切换状态CanExecute()命令。

public class ModelCommand : ICommand
{
    #region Constructors

    public ModelCommand(Action<object> execute)
        : this(execute, null) { }

    public ModelCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return _canExecute != null ? _canExecute(parameter) : true;
    }

    public void Execute(object parameter)
    {
        if (_execute != null)
            _execute(parameter);
    }

    public void OnCanExecuteChanged()
    {
        CanExecuteChanged(this, EventArgs.Empty);
    }

    #endregion

    private readonly Action<object> _execute = null;
    private readonly Predicate<object> _canExecute = null;
}

回答by Hyman_tux

@Carlo I really like your implementation of this, but I wanted to share my version and how to use it in my ViewModel

@Carlo 我真的很喜欢你的实现,但我想分享我的版本以及如何在我的 ViewModel 中使用它

First implement ICommand

首先实现 ICommand

public class Command : ICommand
{
    public delegate void ICommandOnExecute();
    public delegate bool ICommandOnCanExecute();

    private ICommandOnExecute _execute;
    private ICommandOnCanExecute _canExecute;

    public Command(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod = null)
    {
        _execute = onExecuteMethod;
        _canExecute = onCanExecuteMethod;
    }

    #region ICommand Members

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute?.Invoke() ?? true;
    }

    public void Execute(object parameter)
    {
        _execute?.Invoke();
    }

    #endregion
}

Notice I have removed the parameter from ICommandOnExecuteand ICommandOnCanExecuteand added a null to the constructor

注意我已经从ICommandOnExecuteICommandOnCanExecute 中删除了参数,并在构造函数中添加了一个空值

Then to use in the ViewModel

然后在ViewModel中使用

public Command CommandToRun_WithCheck
{
    get
    {
        return new Command(() =>
        {
            // Code to run
        }, () =>
        {
            // Code to check to see if we can run 
            // Return true or false
        });
    }
}

public Command CommandToRun_NoCheck
{
    get
    {
        return new Command(() =>
        {
            // Code to run
        });
    }
}

I just find this way cleaner as I don't need to assign variables and then instantiate, it all done in one go.

我只是觉得这种方式更干净,因为我不需要分配变量然后实例化,这一切都一次性完成。