2014-04-16 14 views
0

В моей модели ViewModel я реализовал интерфейс IDataErrorInfo (вместе с INotifyPropertyChanged). Проверка ввода работает по назначению, у меня нет проблем.Связывание команд WPF с проверкой входных данных - как включить кнопку «Сохранить», только если все введенные значения действительны.

У меня есть это свойство как часть IDataErrorInfo public string Error { get { return this[null]; } } В моем понимании, Error должен быть пустым, если все проверенные входы проходят проверку, поэтому я передаю это как мой метод CanExecute

return !string.IsNullOrEmpty(Error); 

Но, моя кнопка «сохранить» никогда не включается. Мои соображения состоят в том, что CanExecuteChanged никогда не получает тригер. Если это правда, где и как я должен его запускать?


Это мой класс RelayCommand. Я пробовал другие способы реализации, но результаты были одинаковыми. Я думаю, что это работает, потому что кнопка «сохранить» включена, если я не передаю метод CanExecute конструктору.

public class RelayCommand : ICommand 
{ 
    private readonly Action execute; 
    private readonly Func<bool> canExecute; 

    public RelayCommand(Action execute, Func<bool> canExecute = null) 
    { 
     this.execute = execute; 
     this.canExecute = canExecute; 
    } 

    public bool CanExecute(object parameter) 
    { 
     return canExecute == null || canExecute();  
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void Execute(object parameter) { execute(); } 
} 

Кнопка "сохранить":

<Button Content="Save" Command="{Binding InsertCommand}"/> 

InsertCommand:

public RelayCommand InsertCommand { get; internal set; } 

В конструкторе ViewModel:

InsertCommand = new RelayCommand(ExecuteInsert, CanExecuteInsert); 

CanExecute:

bool CanExecuteInsert() 
{ 
    return !string.IsNullOrEmpty(Error); 
} 
+0

Показать InsertCommand, как он был инициализирован, и его CanExecute –

+0

@lll Добавил его в конец. –

ответ

4

Вы действительно не добавили достаточно кода для того, чтобы точно рассказать вам, в чем проблема. Однако вы принимаете правильный подход. Я также использовать интерфейс IDataErrorInfo, но я добавил некоторые дополнительные свойства в мой базовый класс, который реализует это:

public string Error // actual IDataErrorInfo Member 
{ 
    get 
    { 
     if (!HasError) return string.Empty; 
     StringBuilder errors = new StringBuilder(); 
     foreach (string error in Errors) errors.AppendUniqueOnNewLineIfNotEmpty(error); 
     return errors.ToString(); 
    } 
} 

public virtual ObservableCollection<string> Errors 
{ 
    get { return errors; } 
} 

public virtual bool HasError 
{ 
    get { return Errors != null && Errors.Count > 0; } 
} 

Errors коллекция просто позволяет мне поддерживать несколько ошибок одновременно и HasError просто говорит мне, если есть какая-либо ошибка или не. Errors коллекция заполняется с использованием IDataErrorInfo индексатор каждого типа данных:

public override ObservableCollection<string> Errors 
{ 
    get 
    { 
     errors = new ObservableCollection<string>(); 
     errors.AddUniqueIfNotEmpty(this["Title"]); 
     errors.AddUniqueIfNotEmpty(this["Artist"]); 
     ... 
     errors.AddUniqueIfNotEmpty(this["DealerPrice"]); 
     return errors; 
    } 
} 

Так, чтобы ответить на ваш фактический вопрос, я бы обрабатывать CanExecute функциональность Save Command, как это:

public override ICommand Save 
    { 
     get { return new ActionCommand(action => SaveCommand(), canExecute => 
      CanSave(DigitalServiceProviderPriceTier)); } 
    } 
... 
    private bool CanSave(DigitalServiceProviderPriceTier digitalServiceProviderPriceTier) 
    { 
     return digitalServiceProviderPriceTier != null && 
      digitalServiceProviderPriceTier.HasChanges && 
      !digitalServiceProviderPriceTier.HasError; // <-- Important part 
    } 

Итак, это кажется, что вы делаете это в почти таким же образом - мои дополнительные свойства, конечно, необязательны. Если ваше свойство Error никогда не пусто, тогда я бы сказал, что что ваша проблема связана с. Начните с отладки и узнайте, какое значение на самом деле имеет ... возможно, там всегда есть ошибка, которой не должно быть?

Ahhhh ... Я только что заметил ваш код Error ...что ваша проблема:

public string Error { get { return this[null]; } } 

Вы вызываете индексатор со значением null, так что код в ваш индексатор возвращается на самом деле то, что ваше Error значение свойства будет. Индексатор должен также возвращать пустую строку, если нет ошибок проверки:

public override string this[string propertyName] 
{ 
    get 
    { 
     string error = string.Empty; 
     if (propertyName == "SomePropertyName" && SomePropertyName.IsNullOrEmpty()) 
      error = "You must enter some property."; 
     if (propertyName == "OtherPropertyName" && OtherPropertyName.Length != 3) 
      error = "The OtherPropertyName must be 3 characters long."; 
     ... 
     return error; 
    } 
} 

Затем в Error собственности, вы должны вызвать фактических имена свойств, которые вы хотите проверить, как я сделал в моей Errors собственности и неnull. Таким образом, в приведенном выше примере, вы могли бы назвать что-то подобное в вашей Error собственности:

string error = this["SomePropertyName"]; 
if (error == string.Empty) error = this["OtherPropertyName"]; 
return error; 

Еще раз, я написал слишком много информации ... Я просто надеюсь, что все это имеет смысл для вас, и вы не собираюсь возвращаться с десятками новых вопросов. Надеюсь, это достаточно ясно.

+0

Я начал с WPF совсем недавно, и во всех примерах для IDataErrorInfo свойство Error было реализовано именно так. Он работает для проверки, поэтому я не думал, что это проблема. Я попробую ваши предложения, когда вернусь домой. –

+0

Это сработало, спасибо! У меня также был логический недостаток: 'return! String.IsNullOrEmpty (Error);' Он должен идти без отрицания, конечно. –