2009-09-23 334 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