Skip to content

Inheriting your class from IValidatableObject

Vsevolod Pilipenko edited this page Jul 19, 2022 · 2 revisions

Inheritors of IValidatableObject must also implement INotifyPropertyChanged and INotifyDataErrorInfo.

If your class has already inherited INotifyPropertyChanged you can just add next code to your class (this code from ValidatableObject class):

private IObjectValidator? _objectValidator;

/// <inheritdoc />
public IObjectValidator? Validator
{
    get => _objectValidator;
    set
    {
        _objectValidator?.Dispose();
        _objectValidator = value;
        _objectValidator?.Revalidate();
    }
}

/// <inheritdoc />
public virtual void OnPropertyMessagesChanged(string propertyName)
{
    ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}


#region INotifyDataErrorInfo

/// <inheritdoc />
bool INotifyDataErrorInfo.HasErrors => Validator?.IsValid == false || Validator?.HasWarnings == true;

/// <inheritdoc />
public event EventHandler<DataErrorsChangedEventArgs>? ErrorsChanged;

/// <inheritdoc />
IEnumerable INotifyDataErrorInfo.GetErrors(string? propertyName)
{
    if (Validator == null)
        return Array.Empty<ValidationMessage>();
    
    return string.IsNullOrEmpty(propertyName)
        ? Validator.ValidationMessages
        : Validator.GetMessages(propertyName!);
}

#endregion

If your class doesn't inherit from INotifyPropertyChanged, you can easily define it with additional code (which taken from BaseNotifyPropertyChanged class):

/// <inheritdoc />
public event PropertyChangedEventHandler? PropertyChanged;

/// <summary>
/// Raise <see cref="PropertyChanged" /> event.
/// </summary>
/// <param name="propertyName">Name of property.</param>
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

/// <summary>
/// If new value not equal old, set new value and raise <see cref="PropertyChanged" /> event.
/// </summary>
/// <typeparam name="TProp">Type of property.</typeparam>
/// <param name="field">Field of property.</param>
/// <param name="value">New value.</param>
/// <param name="propertyName">Name of property.</param>
protected virtual void SetAndRaiseIfChanged<TProp>(ref TProp field, TProp value, [CallerMemberName] string? propertyName = null)
{
    if (Equals(field, value))
        return;

    field = value;
    OnPropertyChanged(propertyName);
}