2009-09-23 3 views
39

그래서이 특정 MVVM 구현에서는 몇 가지 명령이 필요합니다. ICommand 클래스를 하나씩 구현하는 것에 지쳐 있었기 때문에 해결책을 생각해 냈습니다.하지만 얼마나 좋은지 모릅니다. 따라서 WPF 전문가의 의견을 크게 환영 할 것입니다. 그리고 더 나은 솔루션을 제공 할 수 있다면 더욱 좋습니다!WPF ICommand MVVM 구현

하나의 ICommand 클래스와 매개 변수로 하나의 대리자를 사용하는 두 명의 대리자는 하나의 대리자가 void (OnExecute의 경우)이고 다른 하나가 (OnCanExecute의 경우)입니다. 따라서 ViewModel 클래스에서 호출되는 ICommand의 생성자에서 두 메서드를 보내고 각 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 –

답변

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

+2

나는 MVVM과 함께 일하고 있기 때문에이 접근법을 사용하고 있는데, 매력처럼 작동한다.) – japf

+0

메신저도이 도구를 사용하고 있는데, 유일한 단점은 명령에 할당 된 키보드 단축키가 없다는 것이다. 어떤 아이디어? –

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 : 지금 ICommand의 상용구 코드의 문제를 해결 http://www.codeproject.com/Articles/101881/Executing-Command-Logic-in-a-View-Model 같은 몇 가지 라이브러리를 존재가 보인다.

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을 생성자에 추가했습니다.

나는 변수를 할당하고 모든 것이 한 번에 수행, 인스턴스화 할 필요가 없습니다 0

그럼 난 그냥 청소기이 방법을 찾을

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