2012-03-11 60 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; 
    } 
} 

在上述视图模型中所用的人类型定义如下:

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}" /> 

当我做它绑定到人的属性,如名字,然后点击保存,我可以看到在视图模型命令处理程序的变化而变化,以文本框:里面查看如下我用结合的人。但是,如果这些属性中的任何一个在验证失败CanSave永远不会执行,并且按钮永远不会被禁用。

如何在上述场景中禁用基于DelegateCommand的CanExecute动作处理程序的按钮?

回答

2

尝试这与可以改变错误的所有属性:

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

     OnPropertyChanged("Error"); 
    } 
} 

或者

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

      OnPropertyChanged("Error"); 
     } 

您所遇到的问题是,OnPropertyChanged没有被调用的时候,误差变化。

下一步是在创建人员时订阅该人员的属性changed事件,并创建一个处理程序来检查属性changed,然后更改该命令使用的布尔变量。

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 2012-03-15 16:36:06

1

简而言之 - 当您认为您的CanExecute()返回值可以更改时,您应该致电yourDelegateCommand.RaiseCanExecuteChanged()

在你的榜样,您应该通过INotifyPropertyChanged界面,您Person.Error属性更改,订阅通知Person.PropertyChanged事件在ContentViewModel类,当你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}" /> 

如果您还指定PropertyChangedUpdateSourceTrigger,您的保存按钮将您的打字期间更新..

+0

你的意思是这样的私人_person; 公众人 { 得到 { return _person; } set { _person = value; if(_person.Error!= null) SaveCommand.RaiseCanExecuteChanged(); OnPropertyChanged(“Person”); } }当孩子变化时,我没有收到任何人的通知。我认为这是问题。 – Raj 2012-03-11 18:58:58

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

希望这个作品。 :-)

+0

问题在于,当您将TextBox绑定更新为Person.FirstName时,Person永远不会被视为已更改,并且其PropertyChanged事件不会被触发。 – Raj 2012-03-14 14:34:59

+0

在FirstName的setter中,您正在引用一个名为'FirstName'的PropertyChanged事件作为参数吗?所以,当名字改变时,这会得到提升。并在person_PropertyChanged你可以检查参数字符串是否是'FirstName',并做相应的事情 – 2012-03-14 16:12:35

+0

我有个人类的逻辑来验证其列 - 请参阅Person类中实现IDataErrorInfo的索引器。在ViewModel中没有重复逻辑的情况下实现验证的最佳方式是什么?当我准备在ViewModel中实现某种验证时,您的建议就可以工作 - 在这种情况下,IDataErrorInfo是无用的。 – Raj 2012-03-15 16:18:16

相关问题