ICommand MVVM实现

问题描述 投票:58回答:4

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

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

它确实很好,但是我不确定这是不好的方法,还是有更好的方法。以下是完整的代码,我们将不胜感激任何输入,甚至可以是负面的,但请保持建设性。

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
}
c# .net wpf mvvm icommand
4个回答
69
投票

这几乎与Karl Shifflet demonstratedRelayCommand的方式相同,其中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);
    }
}

然后可以用作...

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

阅读更多:Josh Smith (introducer of RelayCommand): Patterns - WPF Apps With The MVVM Design Pattern


14
投票

我已经写过有关ICommand接口的RelayCommand

[想法-创建一个包含两个委托的通用命令:一个在调用article时调用,第二个检查是否可以执行命令ICommand.Execute (object param)

需要切换事件(ICommand.CanExecute (object param))的方法。从用户界面元素中调用它以切换状态CanExecuteChanged命令。

CanExecute()

10
投票

我刚刚创建了一个小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; } ,显示了如何在配置样式上以约定的方式实现命令。但是,它需要Reflection.Emit()可用。支持代码可能看起来有些怪异,但一旦编写,便可以使用很多次。

Teaer:

example

}

UPDATE:现在似乎存在一些库,例如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); } } ,可以解决ICommand样板代码的问题。


3
投票

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

第一个实现ICommand

http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model

注意,我已经从ICommandOnExecuteICommandOnCanExecute中删除了该参数,并向构造函数添加了null

然后在ViewModel中使用

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
}

我只是发现这种方式更整洁,因为我不需要分配变量然后实例化,所有这些都可以一次性完成。

© www.soinside.com 2019 - 2024. All rights reserved.