2012-03-11 5 views
2

У меня есть класс ViewModel, подобный этому в проекте Prism/WPF.Работа с действием CanExecute DelegateCommand

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 в представлении. Внутри View Я использовал связывание с лицом следующим образом:

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

Когда я вносить изменения в TextBox, который привязываться к свойствам Person как FirstName и нажмите кнопку Сохранить, я мог видеть изменения в обработчик команды ViewModel. Но если какое-либо из этих свойств не выполняется при проверке, CanSave никогда не выполняется, и кнопка никогда не отключается.

Как отключить кнопку на основе обработчика действия CanExecute DelegateCommand в приведенном выше сценарии?

ответ

2

попробовать это со всеми свойствами, которые могут изменить ошибку:

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

     OnPropertyChanged("Error"); 
    } 
} 

Альтернативно

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

      OnPropertyChanged("Error"); 
     } 

Проблема у Вас есть то, что OnPropertyChanged не вызывается, когда изменяется ошибка ,

Следующим шагом является подписка на событие propertychanged человека при его создании и создание обработчика, который проверяет свойство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

1

В двух словах - вы должны позвонить yourDelegateCommand.RaiseCanExecuteChanged(), если вы считаете, что ваше возвращаемое значение CanExecute() может быть изменено.

В вашем примере, вы должны уведомить через INotifyPropertyChanged интерфейс, который изменяется ваше Person.Error свойства, при подписке Person.PropertyChanged события в вашем ContentViewModel класса и вызвать SaveCommand.RaiseCanExecuteChanged() каждый раз, когда ваш Person.Error изменяются. Пожалуйста, будьте осторожны - в вашем сценарии Person.Error не пересчитывается автоматически, когда изменяется, например, Person.FirstName - вы должны сделать это вручную.

ОБНОВЛЕНО:

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

Если вы также указать PropertyChanged, как UpdateSourceTrigger, ваша кнопка сохранения будет обновляться во время набора текста ..

+0

Вы имеете в виду нечто вроде этого частного лица _person; общественное лицо Лицо { Получать { возвращение _person; } { _person = value; if (_person.Error! = Null) SaveCommand.RaiseCanExecuteChanged(); OnPropertyChanged («Человек»); } } Я не получаю уведомления в Людей, когда это изменение ребенка. Я думаю, что здесь проблема. – 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() 
} 

Надеется, что это работает. :-)

+0

Проблема заключается в том, что при обновлении привязки TextBox к Person.FirstName Person никогда не рассматривается как измененный, и его событие PropertyChanged не будет запущено. – Raj

+0

В установщике FirstName вы поднимаете событие PropertyChanged с именем «FirstName» в качестве аргумента? Поэтому, когда меняется FirstName, это поднимается. И в person_PropertyChanged вы можете проверить, является ли строка аргумента «FirstName» и что-то делать. –

+0

У меня есть логика в классе для проверки ее столбцов - см. Индекс в классе Person, который реализует IDataErrorInfo. Каким будет лучший способ реализовать проверку без дублирования логики в ViewModel? Ваше предложение работает, когда я готов выполнить некоторую проверку в ViewModel - в таком случае IDataErrorInfo бесполезен. – Raj

Смежные вопросы