Это действительно зависит от вашей реализации IDataErrorInfo. Если вы основываете его на словаре сообщений об ошибках, вы можете контролировать, когда запускается проверка, которая добавляет этот список. Обычно вы хотели бы сделать это из ваших установщиков свойств (как всякий раз, когда вы вызываете PropertyChange), здесь вызывается CheckValidationState:
public string this[string columnName]
{
get
{
return ValidateProperty(columnName);
}
}
public Dictionary<string, string> Errors { get; private set; }
protected void SetError(string propertyName, string errorMessage)
{
Debug.Assert(!String.IsNullOrEmpty(propertyName), "propertyName is null or empty.");
if (String.IsNullOrEmpty(propertyName))
return;
if (!String.IsNullOrEmpty(errorMessage))
{
if (Errors.ContainsKey(propertyName))
Errors[propertyName] = errorMessage;
else
Errors.Add(propertyName, errorMessage);
}
else if (Errors.ContainsKey(propertyName))
Errors.Remove(propertyName);
NotifyPropertyChanged("Errors");
NotifyPropertyChanged("Error");
NotifyPropertyChanged("Item[]");
}
protected virtual string ValidateProperty(string propertyName)
{
return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null;
}
protected virtual bool CheckValidationState<T>(string propertyName, T proposedValue)
{
// your validation logic here
}
Затем вы также можете включить метод, который проверяет все ваши свойства (например, во время сохранения):
protected bool Validate()
{
if (Errors.Count > 0)
return false;
bool result = true;
foreach (PropertyInfo propertyInfo in GetType().GetProperties())
{
if (!CheckValidationState(propertyInfo.Name, propertyInfo.GetValue(this, null)))
result = false;
NotifyPropertyChanged(propertyInfo.Name);
}
return result;
}
UPDATE:
Я бы рекомендовал поместить приведенный выше код в базовый класс ViewModel, чтобы вы могли использовать его повторно. Затем вы можете создать производный класс следующим образом:
public class SampleViewModel : ViewModelBase
{
private string _firstName;
public SampleViewModel()
{
Save = new DelegateCommand<object>(SaveExecuted);
}
public DelegateCommand<object> Save { get; private set; }
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
return;
CheckValidationState("FirstName", value);
_firstName = value;
NotifyPropertyChanged("FirstName");
}
}
public void SaveExecuted(object obj)
{
bool isValid = Validate();
MessageBox.Show(isValid ? "Saved" : "Validation Error. Save canceled"); // TODO: do something appropriate to your app here
}
protected override bool CheckValidationState<T>(string propertyName, T proposedValue)
{
// your validation logic here
if (propertyName == "FirstName")
{
if (String.IsNullOrEmpty(proposedValue as String))
{
SetError(propertyName, "First Name is required.");
return false;
}
else if (proposedValue.Equals("John"))
{
SetError(propertyName, "\"John\" is not an allowed name.");
return false;
}
else
{
SetError(propertyName, String.Empty); // clear the error
return true;
}
}
return true;
}
}
В этом случае я использую DelegateCommand для запуска операции сохранения, но это может быть все, что вызывает метод для сохранения. Эта настройка позволяет исходному пустому состоянию отображаться как действительное в пользовательском интерфейсе, но изменение или вызов Save обновляют состояние проверки. Вы также можете получить гораздо более общее и более сложное представление о том, как на самом деле выполнять проверку, так что не все заканчивается одним методом (здесь с некоторыми предположениями о типе), но это упрощено, чтобы было легче начать с .