2009-09-23 52 views
39

Vì vậy, trong triển khai MVVM cụ thể này, tôi cần một vài lệnh. Tôi thực sự đã mệt mỏi với việc triển khai các lớp ICommand từng cái một, vì vậy tôi đã đưa ra một giải pháp, nhưng tôi không biết nó tốt như thế nào, vì vậy đầu vào của bất kỳ chuyên gia WPF nào ở đây sẽ được đánh giá cao. Và nếu bạn có thể cung cấp một giải pháp tốt hơn, thậm chí tốt hơn!WPF ICommand MVVM implementation

Điều tôi đã làm là một lớp ICommand duy nhất và hai đại biểu nhận một đối tượng làm tham số, một đại biểu bị vô hiệu (đối với OnExecute), một bool khác (cho OnCanExecute). Vì vậy, trong constructor của ICommand của tôi (được gọi bởi lớp ViewModel), tôi gửi hai phương thức, và trên mỗi phương thức ICommand tôi gọi các phương thức của các đại biểu.

Nó hoạt động thực sự tốt, nhưng tôi không chắc chắn nếu đây là một cách xấu để làm điều đó, hoặc nếu có một cách tốt hơn. Dưới đây là mã hoàn chỉnh, mọi đầu vào sẽ được đánh giá cao, thậm chí là tiêu cực, nhưng hãy xây dựng.

Cảm ơn !!

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 
} 
+2

Kiểm tra việc triển khai của Karl Shifflet của RelayCommand: http://www.codeproject.com/KB/WPF/ExploringWPFMVVM.aspx –

Trả lời

48

này là gần như giống với cách Karl Shifflet chứng minh một RelayCommand, nơi Execute cháy một định trước Action<T>. Một giải pháp hàng đầu, nếu bạn hỏi tôi.

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

này sau đó có thể được sử dụng như ...

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

Nó trông tương tự như của tôi. Thật thú vị khi biết những ưu và nhược điểm của việc sử dụng nó. Bạn có một liên kết đến bài viết hoặc blog nơi bạn đọc này? – Carlo

+2

Tôi đang sử dụng phương pháp này vì tôi đang làm việc với MVVM và nó hoạt động như một sự quyến rũ;) – japf

+0

im sử dụng nó là tốt, con duy nhất tôi có thể tìm thấy là bạn không có một phím tắt giao cho lệnh. bất kỳ ý tưởng? –

11

Tôi vừa tạo một chút example cho thấy làm thế nào để thực hiện các lệnh trong quy ước về phong cách cấu hình. Tuy nhiên nó yêu cầu Reflection.Emit() để có sẵn. Mã hỗ trợ có vẻ hơi kỳ lạ nhưng một khi được viết, nó có thể được sử dụng nhiều lần.

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

}

CẬP NHẬT: bây giờ có vẻ như tồn tại một số thư viện như http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model rằng giải quyết vấn đề mã boilerplate ICommand.

11

Tôi đã viết số này article về giao diện ICommand. Ý tưởng - tạo ra một lệnh phổ quát mà có hai đại biểu: một được gọi là khi ICommand.Execute (object param) được gọi, lần thứ hai kiểm tra trạng thái của việc bạn có thể thực hiện lệnh (ICommand.CanExecute (object param)) hay không.

Yêu cầu phương thức chuyển sự kiện CanExecuteChanged. Nó được gọi từ các phần tử giao diện người dùng để chuyển đổi lệnh CanExecute() của tiểu bang.

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 Tôi thực sự thích thực hiện lại điều này, nhưng tôi muốn chia sẻ phiên bản của tôi và làm thế nào để sử dụng nó trong ViewModel của tôi

Đầu tiên thực hiện 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 
} 

Thông báo tôi đã gỡ bỏ tham số từ ICommandOnExecuteICommandOnCanExecute và thêm một giá trị null vào hàm tạo

Sau đó, để sử dụng trong 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 
     }); 
    } 
} 

Tôi chỉ tìm cách này sạch như tôi không cần phải gán biến và sau đó nhanh chóng, tất cả được thực hiện trong một đi.

+0

Cảm ơn bạn đã chia sẻ điều này! Nó chắc chắn thú vị để xem những cách khác để giải quyết điều này. Kể từ khi tôi đọc về RelayCommand, tôi đã quyết định áp dụng mẫu đó. Tôi đã không thực hiện WPF trong NĂM, nhưng tôi chắc chắn đã sử dụng RelayCommand trong một vài năm trước khi xu hướng trong công ty của tôi chuyển sang web. – Carlo