2009-09-23 109 views
39

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

我所做的是一个ICommand类和两个委托,它们将一个对象作为参数,一个委托是void(用于OnExecute),另一个是用于OnCanExecute的bool。所以在我的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 
} 
+2

查看Karl Shifflet的RelayCommand实现:http://www.codeproject.com/KB/WPF/ExploringWPFMVVM.aspx – 2009-09-24 00:26:03

回答

48

这几乎是相同的卡尔Shifflet如何表现出RelayCommand,其中Execute火灾预定Action<T>。一个顶尖的解决方案,如果你问我。

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

    public RelayCommand(Predicate<object> canExecute, Action<object> execute) 
    { 
     this._canExecute = canExecute; 
     this._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; 
     } 
    } 
} 
+0

它的外观与我的相似。了解使用它的优点和缺点会很有趣。你有阅读过这篇文章或博客的链接吗? – Carlo 2009-09-23 22:26:35

+2

我使用这种方法,因为我正在使用MVVM,它的功能就像一个魅力;) – japf 2009-09-24 07:12:13

+0

即时通讯使用它,唯一的我可以找到的是,你没有分配给该命令的键盘快捷方式。有任何想法吗? – 2009-09-24 07:14:57

11

我刚刚创建了一个小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); 
    } 
} 

}

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

11

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

这个想法 - 创建一个需要两个代表的通用命令:一个在调用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; 
} 
1

@Carlo我很喜欢你的这个实现,但我想分享我的版本,以及如何在我的视图模型使用

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

然后在视图模型

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

用我只是觉得这种方式更清洁,因为我不需要给变量赋值,然后实例,这一切在一气呵成完成。

+0

感谢分享!看到解决这个问题的其他方法确实很有趣。自从我阅读RelayCommand后,我决定采用这种模式。我没有在年份做过WPF,但在我公司的这个趋势转向网络之前,我确实使用了RelayCommand几年。 – Carlo 2017-11-08 22:18:21