2012-03-11 17 views
2

私はPrism/WPFプロジェクトでこのようなViewModelクラスを持っています。DelegateCommandのCanExecuteアクションの操作

public class ContentViewModel : ViewModelBase, IContentViewModel 
{ 
    public ContentViewModel(IPersonService personService) 
    { 
     Person = personService.GetPerson(); 
     SaveCommand = new DelegateCommand(Save, CanSave); 
    } 

    public Person Person { get; set; } 

    public DelegateCommand SaveCommand { get; set; } 

    private void Save() 
    { 
     // Save actions here... 
    } 

    private bool CanSave() 
    { 
     return Person.Error == null; 
    } 
} 

次のように上記のViewModelに使用者タイプが定義されている:

public class Person : INotifyPropertyChanged, IDataErrorInfo 
{ 
    private string _firstName; 
    public string FirstName 
    { 
     get { return _firstName; } 
     set 
     { 
      _firstName = value; 
      OnPropertyChanged("FirstName"); 
     } 
    } 

    // other properties are implemented in the same way as above... 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    private string _error; 
    public string Error 
    { 
     get 
     { 
      return _error; 
     } 
    } 

    public string this[string columnName] 
    { 
     get 
     { 
      _error = null; 
      switch (columnName) 
      { 
       // logic here to validate columns... 
      } 
      return _error; 
     } 
    } 
} 

ContentViewModelのインスタンスがビューのDataContextのように設定されています。

<TextBox Text="{Binding Person.FirstName, ValidatesOnDataErrors=True}" /> 
<Button Content="Save" Command="{Binding SaveCommand}" /> 

私はViewModelにコマンドハンドラの変化を見ることができました姓のような人のプロパティにバインドさ、[保存]をクリックしているテキストボックスに変更を加えた場合:ビュー内私は次のように人への結合を使用しました。しかし、これらのプロパティのいずれかが検証に失敗した場合、CanSaveは実行されず、ボタンが無効になることはありません。

上記のシナリオでは、DelegateCommandのCanExecuteアクションハンドラに基づいてボタンを無効にするにはどうすればよいですか?

答えて

2

すると、エラーを変更することができ、すべてのプロパティでこれを試してみてください。

次の手順は、作成時にpersonのpropertychangedイベントを購読し、プロパティが変更されたことを確認してコマンドが使用するブール変数を変更するハンドラを作成することです。

public ContentViewModel(IPersonService personService) 
{ 
    Person = personService.GetPerson(); 
    Person.PropertyChanged+= PersonPropertyChangedHandler; 
    SaveCommand = new DelegateCommand(Save, personHasError); 
} 

bool personHasError = false; 
void PersonPropertyChangedHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
{ 
    if (e.PropertyName == "Error") 
    { 
     if(Person.Error == null) 
      personHasError = true; 
     else 
      personHasError = false; 
    } 
} 

希望します。私は手でこれを作り、それをチェックしなかったので、そのバギーか何かがそれを訂正するかどうか知らせてください

+0

問題はOnPropertyChangedがエラーが変更されたときには呼び出されません。 +1 – Raj

1

一言で言えば、CanExecute()の戻り値を変更できると思われる場合は、yourDelegateCommand.RaiseCanExecuteChanged()を呼び出す必要があります。あなたの例では

、あなたは、あなたのPerson.Errorプロパティが変更されINotifyPropertyChangedインタフェースを介して通知し、あなたのContentViewModelクラスでPerson.PropertyChangedイベントをサブスクライブして、Person.Errorが変更されるSaveCommand.RaiseCanExecuteChanged()たびに呼び出す必要があります。たとえばPerson.FirstNameが変更された場合、シナリオでPerson.Errorは自動的に再計算されません。手動で行う必要があります。

更新日:

public class ContentViewModel : ViewModelBase, IContentViewModel 
{ 
    public ContentViewModel(IPersonService personService) 
    { 
     Person = personService.GetPerson(); 
     Person.PropertyChanged += Person_PropertyChanged; 
     SaveCommand = new DelegateCommand(Save, CanSave); 
    } 

    private void PersonPropertyChangedHandler(object sender, PropertyChangedEventArgs e) 
    { 
     SaveCommand.RaiseCanExecuteChanged(); 
    } 

    private void Save() 
    { 
     // Save actions here... 
    } 

    private bool CanSave() 
    { 
     return IsErrorPresented(Person); 
    } 

    private bool IsErrorPresented(object o) 
    { 
     if (!(o is IDataErrorInfo)) 
      return false; 

     var propNames = o.GetType() 
      .GetProperties(BindingFlags.Public | BindingFlags.Instance) 
      .Select(p => p.Name); 

     var o2 = (o as IDataErrorInfo); 

     var errors = propNames.Select(p => o2[p]) 
      .Where(p => !String.IsNullOrEmpty(p)) 
      .ToList(); 

     ValidationSummary.ErrorMessages = errors; 

     return errors.Count > 0; 
    } 
} 

<TextBox Text="{Binding Person.FirstName, 
         UpdateSourceTrigger=PropertyChanged, 
         ValidatesOnDataErrors=True, 
         ValidatesOnExceptions=True, 
         NotifyOnValidationError=True}" /> 
<Button Content="Save" Command="{Binding SaveCommand}" /> 

あなたもUpdateSourceTriggerとしてPropertyChangedを指定する場合は、あなたの保存ボタンは、あなたのタイピング中に更新されます。..

+0

あなたはこの個人のPerson _personのようなものを意味します。 public Person Person { get { return _person; } セット { _person = value; if(_person.Error!= null) SaveCommand.RaiseCanExecuteChanged(); OnPropertyChanged( "Person"); } }子が変更されたとき、私は人に通知を受けません。私はそれがここの問題だと思う。 – Raj

4

をContentViewModelのコンストラクタで

この行を追加します。
public ContentViewModel(IPersonService personService) 
{ 
    //GetPerson 
    Person.PropertyChanged +=person_PropertyChanged; 
} 

そして、そのイベントを処理するメソッドを記述します。このメソッドでは、 CommandManager.InvalidateRequerySuggested()またはSaveCommand.RaiseCanExecuteChanged()

private void person_PropertyChanged(object sender, EventArgs args) 
{ 
    CommandManager.InvalidateRequerySuggested(); 
    //SaveCommand.RaiseCanExecuteChanged() 
} 

・ホープ、この作品。また

 switch (columnName) 
     { 
      // logic here to validate columns... 

      OnPropertyChanged("Error"); 
     } 

あなたが持っている問題は、ときにエラーが変更OnPropertyChangedをが呼び出されていないということです

public string FirstName 
{ 
    get { return _firstName; } 
    set 
    { 
     _firstName = value; 
     OnPropertyChanged("FirstName"); 

     OnPropertyChanged("Error"); 
    } 
} 

::-)

+0

Person.FirstName PersonへのTextBoxバインディングを更新すると、Personが変更されたとして処理されず、そのPropertyChangedイベントが発生しなくなるという問題があります。 – Raj

+0

FirstNameのセッターでは、FirstNameを引数としてPropertyChangedイベントを発生させていますか? FirstNameが変更されると、これが発生します。 person_PropertyChangedでは、引数文字列が 'FirstName'であるかどうかをチェックし、それに応じて処理を行うことができます。 –

+0

Personクラスのロジックを検証して列を検証します。IDataErrorInfoを実装しているPersonクラスのインデクサーを参照してください。 ViewModelのロジックを複製せずに検証を実装する最良の方法は何でしょうか?あなたの提案は、ViewModelで何らかのバリデーションを実装する準備ができているときに機能します。そのような場合、IDataErrorInfoは役に立たないです。 – Raj

関連する問題