2016-11-23 1 views
2

에서 재평가, 나는 MVVM 패턴 사용자 간의 공통의 문제를 보이는 없애려고 해요.WPF MVVM 명령 CanExecute는 만 WPF</strong><strong>에 <strong>MVVM</strong> 프로젝트를 리팩토링 포커스 변경

보기가 있는데 누가 입니다. DataContext입니다. MyViewModel입니다. 다음은 Execute와 CanExecute를 구현하는 Command로 묶인 버튼입니다.

XAML :

<Button Command="{Binding ConnectCommand}"/> 

MyViewModelConnectCommand를 노출 :

public ICommand ConnectCommand 
    { 
     get { return new DelegateCommand(() => Connect(),() => IsConnectEnabled); } 
    } 

MyViewMo을 (끝 DelegateCommand의 정의는 내가 사용하고 있습니다) 클래스는 가의 인터페이스

public class MyViewModel : INotifyPropertyChanged 
{ 
    #region INotifyPropertyChanged Members 
    public event PropertyChangedEventHandler PropertyChanged; 
    public void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    #endregion 

    #region rest of the class   
} 

CanExecute 부분에서 INotifyPropertyChanged 구현

public bool IsConnectEnabled 
     { 
      get 
      { 
       return (isDisconnected && null!=selectedDevice && 0<selectedDevice.Length); 
      } 
     } 

MyViewModel : 델는 명령의 CanExecute 부분에 사용 을 IsConnectEnabled 속성을 노출합니다 명령은 응용 프로그램의 포커스 변경시에만 평가됩니다 (즉, 클릭 할 때마다). UpdateSourceTrigger은 기본적으로 으로 설정되어 있습니다. 따라서 PropertyChanged, 현재 해결책은 PropertyChanged 이벤트를 코드의 몇 군데에 수동으로 발생시키는 것입니다. 그러나 나는 더 잘하고 싶다. IsConnectEnabled 값이 변경 될 때마다이 작업을 자동으로 수행하고 싶다.

WPF와 MVVM 패턴이이 문제에 대한 해결책을 제공합니까?

/// <summary> 
    ///  This class allows delegating the commanding logic to methods passed as parameters, 
    ///  and enables a View to bind commands to objects that are not part of the element tree. 
    /// </summary> 
    public class DelegateCommand : ICommand 
    { 
     /// <summary> 
     ///  Constructor 
     /// </summary> 
     public DelegateCommand(Action executeMethod) 
      : this(executeMethod, null, false) 
     { 
     } 

     /// <summary> 
     ///  Constructor 
     /// </summary> 
     public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod) 
      : this(executeMethod, canExecuteMethod, false) 
     { 
     } 

     /// <summary> 
     ///  Constructor 
     /// </summary> 
     public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled) 
     { 
      if (executeMethod == null) 
      { 
       throw new ArgumentNullException("executeMethod"); 
      } 

      _executeMethod = executeMethod; 
      _canExecuteMethod = canExecuteMethod; 
      _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled; 
     } 

     #region Public Methods 

     /// <summary> 
     ///  Method to determine if the command can be executed 
     /// </summary> 
     public bool CanExecute() 
     { 
      if (_canExecuteMethod != null) 
      { 
       return _canExecuteMethod(); 
      } 
      return true; 
     } 

     /// <summary> 
     ///  Execution of the command 
     /// </summary> 
     public void Execute() 
     { 
      if (_executeMethod != null) 
      { 
       _executeMethod(); 
      } 
     } 

     /// <summary> 
     ///  Property to enable or disable CommandManager's automatic requery on this command 
     /// </summary> 
     public bool IsAutomaticRequeryDisabled 
     { 
      get 
      { 
       return _isAutomaticRequeryDisabled; 
      } 
      set 
      { 
       if (_isAutomaticRequeryDisabled != value) 
       { 
        if (value) 
        { 
         CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers); 
        } 
        else 
        { 
         CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers); 
        } 
        _isAutomaticRequeryDisabled = value; 
       } 
      } 
     } 

     /// <summary> 
     ///  Raises the CanExecuteChaged event 
     /// </summary> 
     public void RaiseCanExecuteChanged() 
     { 
      OnCanExecuteChanged(); 
     } 

     /// <summary> 
     ///  Protected virtual method to raise CanExecuteChanged event 
     /// </summary> 
     protected virtual void OnCanExecuteChanged() 
     { 
      CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers); 
     } 

     #endregion 

     #region ICommand Members 

     /// <summary> 
     ///  ICommand.CanExecuteChanged implementation 
     /// </summary> 
     public event EventHandler CanExecuteChanged 
     { 
      add 
      { 
       if (!_isAutomaticRequeryDisabled) 
       { 
        CommandManager.RequerySuggested += value; 
       } 
       CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2); 
      } 
      remove 
      { 
       if (!_isAutomaticRequeryDisabled) 
       { 
        CommandManager.RequerySuggested -= value; 
       } 
       CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value); 
      } 
     } 

     bool ICommand.CanExecute(object parameter) 
     { 
      return CanExecute(); 
     } 

     void ICommand.Execute(object parameter) 
     { 
      Execute(); 
     } 

     #endregion 

     #region Data 

     private readonly Action _executeMethod = null; 
     private readonly Func<bool> _canExecuteMethod = null; 
     private bool _isAutomaticRequeryDisabled = false; 
     private List<WeakReference> _canExecuteChangedHandlers; 

     #endregion 
    } 

    /// <summary> 
    ///  This class allows delegating the commanding logic to methods passed as parameters, 
    ///  and enables a View to bind commands to objects that are not part of the element tree. 
    /// </summary> 
    /// <typeparam name="T">Type of the parameter passed to the delegates</typeparam> 
    public class DelegateCommand<T> : ICommand 
    { 
     #region Constructors 

     /// <summary> 
     ///  Constructor 
     /// </summary> 
     public DelegateCommand(Action<T> executeMethod) 
      : this(executeMethod, null, false) 
     { 
     } 

     /// <summary> 
     ///  Constructor 
     /// </summary> 
     public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod) 
      : this(executeMethod, canExecuteMethod, false) 
     { 
     } 

     /// <summary> 
     ///  Constructor 
     /// </summary> 
     public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled) 
     { 
      if (executeMethod == null) 
      { 
       throw new ArgumentNullException("executeMethod"); 
      } 

      _executeMethod = executeMethod; 
      _canExecuteMethod = canExecuteMethod; 
      _isAutomaticRequeryDisabled = isAutomaticRequeryDisabled; 
     } 

     #endregion 

     #region Public Methods 

     /// <summary> 
     ///  Method to determine if the command can be executed 
     /// </summary> 
     public bool CanExecute(T parameter) 
     { 
      if (_canExecuteMethod != null) 
      { 
       return _canExecuteMethod(parameter); 
      } 
      return true; 
     } 

     /// <summary> 
     ///  Execution of the command 
     /// </summary> 
     public void Execute(T parameter) 
     { 
      if (_executeMethod != null) 
      { 
       _executeMethod(parameter); 
      } 
     } 

     /// <summary> 
     ///  Raises the CanExecuteChaged event 
     /// </summary> 
     public void RaiseCanExecuteChanged() 
     { 
      OnCanExecuteChanged(); 
     } 

     /// <summary> 
     ///  Protected virtual method to raise CanExecuteChanged event 
     /// </summary> 
     protected virtual void OnCanExecuteChanged() 
     { 
      CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers); 
     } 

     /// <summary> 
     ///  Property to enable or disable CommandManager's automatic requery on this command 
     /// </summary> 
     public bool IsAutomaticRequeryDisabled 
     { 
      get 
      { 
       return _isAutomaticRequeryDisabled; 
      } 
      set 
      { 
       if (_isAutomaticRequeryDisabled != value) 
       { 
        if (value) 
        { 
         CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers); 
        } 
        else 
        { 
         CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers); 
        } 
        _isAutomaticRequeryDisabled = value; 
       } 
      } 
     } 

     #endregion 

     #region ICommand Members 

     /// <summary> 
     ///  ICommand.CanExecuteChanged implementation 
     /// </summary> 
     public event EventHandler CanExecuteChanged 
     { 
      add 
      { 
       if (!_isAutomaticRequeryDisabled) 
       { 
        CommandManager.RequerySuggested += value; 
       } 
       CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2); 
      } 
      remove 
      { 
       if (!_isAutomaticRequeryDisabled) 
       { 
        CommandManager.RequerySuggested -= value; 
       } 
       CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value); 
      } 
     } 

     bool ICommand.CanExecute(object parameter) 
     { 
      // if T is of value type and the parameter is not 
      // set yet, then return false if CanExecute delegate 
      // exists, else return true 
      if (parameter == null && 
       typeof(T).IsValueType) 
      { 
       return (_canExecuteMethod == null); 
      } 
      return CanExecute((T)parameter); 
     } 

     void ICommand.Execute(object parameter) 
     { 
      Execute((T)parameter); 
     } 

     #endregion 

     #region Data 

     private readonly Action<T> _executeMethod = null; 
     private readonly Func<T, bool> _canExecuteMethod = null; 
     private bool _isAutomaticRequeryDisabled = false; 
     private List<WeakReference> _canExecuteChangedHandlers; 

     #endregion 
    } 

    /// <summary> 
    ///  This class contains methods for the CommandManager that help avoid memory leaks by 
    ///  using weak references. 
    /// </summary> 
    internal class CommandManagerHelper 
    { 
     internal static void CallWeakReferenceHandlers(List<WeakReference> handlers) 
     { 
      if (handlers != null) 
      { 
       // Take a snapshot of the handlers before we call out to them since the handlers 
       // could cause the array to me modified while we are reading it. 

       EventHandler[] callees = new EventHandler[handlers.Count]; 
       int count = 0; 

       for (int i = handlers.Count - 1; i >= 0; i--) 
       { 
        WeakReference reference = handlers[i]; 
        EventHandler handler = reference.Target as EventHandler; 
        if (handler == null) 
        { 
         // Clean up old handlers that have been collected 
         handlers.RemoveAt(i); 
        } 
        else 
        { 
         callees[count] = handler; 
         count++; 
        } 
       } 

       // Call the handlers that we snapshotted 
       for (int i = 0; i < count; i++) 
       { 
        EventHandler handler = callees[i]; 
        handler(null, EventArgs.Empty); 
       } 
      } 
     } 

     internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers) 
     { 
      if (handlers != null) 
      { 
       foreach (WeakReference handlerRef in handlers) 
       { 
        EventHandler handler = handlerRef.Target as EventHandler; 
        if (handler != null) 
        { 
         CommandManager.RequerySuggested += handler; 
        } 
       } 
      } 
     } 

     internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers) 
     { 
      if (handlers != null) 
      { 
       foreach (WeakReference handlerRef in handlers) 
       { 
        EventHandler handler = handlerRef.Target as EventHandler; 
        if (handler != null) 
        { 
         CommandManager.RequerySuggested -= handler; 
        } 
       } 
      } 
     } 

     internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler) 
     { 
      AddWeakReferenceHandler(ref handlers, handler, -1); 
     } 

     internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize) 
     { 
      if (handlers == null) 
      { 
       handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>()); 
      } 

      handlers.Add(new WeakReference(handler)); 
     } 

     internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler) 
     { 
      if (handlers != null) 
      { 
       for (int i = handlers.Count - 1; i >= 0; i--) 
       { 
        WeakReference reference = handlers[i]; 
        EventHandler existingHandler = reference.Target as EventHandler; 
        if ((existingHandler == null) || (existingHandler == handler)) 
        { 
         // Clean up old handlers that have been collected 
         // in addition to the handler that is to be removed. 
         handlers.RemoveAt(i); 
        } 
       } 
      } 
     } 
    } 
} 
+0

WPF 또는 Silverlight? – heltonbiker

+0

@heltonbiker WPF. 감사합니다. 설명에서 더 명확하게했습니다. – Riccardo

+0

"WPF와 MVVM 패턴이이 문제를 해결할 수 있습니까?" 아니. – Will

답변

0

이 적어도 내 WPF 경험, 오래된 버그 동작입니다 :

완성도를 들어, 내가 DelegateCommand를 사용하고 전체 ICommand의 구현을 따른다.

RelayCommand (GalaSoft.MvvmLight.CommandWpf.RelayCommand)의 MvvmLight 구현이이 문제를 해결하는 것으로 나타났습니다.

그들은 이상한 문제를 해결하기 위해 apparently을 대상으로하는 WPF 특정 구현 (!)을 만듭니다.

관련 문제