【问题标题】:ICommand MVVM implementationICommand MVVM 实现
【发布时间】:2010-11-30 22:14:02
【问题描述】:

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

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

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

视图模型:

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


【解决方案1】:

这与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

【讨论】:

  • 它看起来和我的很相似。知道使用它的利弊会很有趣。您是否有指向您阅读本文的文章或博客的链接?
  • 我正在使用这种方法,因为我正在使用 MVVM,它就像一个魅力;)
  • 我也在使用它,我能找到的唯一缺点是您没有为该命令分配键盘快捷键。有什么想法吗?
  • @Aran 我发现为 RelayCommand 分配键盘快捷键的最佳解决方案是 CommandReference。唯一的缺点是您无法在上下文菜单等中自动填充快捷方式。 joyfulwpf.blogspot.com/2009/05/…
  • Josh Smith 链接自 2021 年 1 月 22 日起已断开。:(
【解决方案2】:

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

预告片:

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

}

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

【讨论】:

    【解决方案3】:

    我已经写了这个article 关于ICommand 接口。

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

    需要切换事件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;
    }
    

    【讨论】:

      【解决方案4】:

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

      首先实现 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
      }
      

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

      然后在 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
              });
          }
      }
      

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

      【讨论】:

      • 感谢分享!看到解决这个问题的其他方法肯定很有趣。自从我读到 RelayCommand,我就决定采用这种模式。我已经有好几年没做过 WPF了,但是在我公司的趋势转向 Web 之前,我肯定使用了几年的 RelayCommand。
      猜你喜欢
      • 2013-10-28
      • 2010-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-31
      • 2011-01-09
      • 1970-01-01
      相关资源
      最近更新 更多