Работа с действием CanExecute DelegateCommand - PullRequest
2 голосов
/ 11 марта 2012

У меня есть такой класс ViewModel в проекте Prism / WPF.

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

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

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

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

Ответы [ 3 ]

4 голосов
/ 14 марта 2012

В конструкторе 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()
}

Надеюсь, это работает.: -)

2 голосов
/ 15 марта 2012

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

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

        OnPropertyChanged("Error");
    }
}

В качестве альтернативы

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

            OnPropertyChanged("Error");
        }

Проблема, с которой вы столкнулись, заключается в том, что OnPropertyChanged не вызывается при изменении ошибки.

Следующим шагом является подписка на событие propertyaged человека при его создании и создание обработчика, который проверяет измененное свойство и затем изменяет логическую переменную, которую использует команда.

 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;
    } 
}

Надеюсь, это работает.Я построил это вручную и не проверял, поэтому дайте мне знать, если он глючит или что-то еще, и плохо исправлю это

1 голос
/ 11 марта 2012

В двух словах - вы должны позвонить 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, ваша кнопка сохранения будет обновлена ​​во время ввода.

...