question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Please implement INotifyDataErrorInfo on Screen/PropertyChangedBase (3.0?)

See original GitHub issue

This framework supports many platforms, so it would be great if it would also support the INotifyDataErrorInfo interface to offer custom validation through an async interface which wpf can bind to.

I am willing to contribute if you would consider this to be an option and are open for contributions.

I’ll show what i’ve done so far. It works but apart from the _errorsChanged handler having a strong referenced handler right now

I think a built in solution would be great to have

public class ValidationModelBase : PropertyChangedBase, INotifyDataErrorInfo
{
    private Dictionary<string, List<ValidationResult>> _errors = new Dictionary<string, List<ValidationResult>>();

    bool INotifyDataErrorInfo.HasErrors => _errors.Keys.Any();

    [field:NonSerialized]
    private EventHandler<DataErrorsChangedEventArgs> _errorsChanged;

    event EventHandler<DataErrorsChangedEventArgs> INotifyDataErrorInfo.ErrorsChanged
    {
        add { _errorsChanged += value; }
        remove { _errorsChanged -= value; }
    }

    protected void RaiseErrorsChanged(string propertyName)
    {
        _errorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
    }

    IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
    {
        if (!_errors.ContainsKey(propertyName))
            yield break;

        foreach (var result in _errors[propertyName])
        {
            yield return result;
        }
    }

    private Dictionary<string, CancellationTokenSource> _validateCts = new Dictionary<string, CancellationTokenSource>();

    public bool SetValue<T>(ref T backingField, T value, string propertyName)
    {
        if (object.Equals(backingField, value))
            return false;

        backingField = value;

        var validationDelay = GetValidationDelay(propertyName);
        if (validationDelay > 0)
        {
            CancellationTokenSource source;
            if (_validateCts.TryGetValue(propertyName, out source))
            {
                source.Cancel();
                source = new CancellationTokenSource();
                _validateCts[propertyName] = source;
            }
            else
            {
                source = new CancellationTokenSource();
                _validateCts.Add(propertyName, source);
            }

            Task.Delay(validationDelay, source.Token).ContinueWith(d =>
            {
                ValidateProperty(value, propertyName);
                NotifyOfPropertyChange(propertyName);
            }, source.Token);
        }
        else
        {
            Task.Run(() =>
            {
                ValidateProperty(value, propertyName);
                NotifyOfPropertyChange(propertyName);
            });
        }

        return true;
    }

    private void ValidateProperty<T>(T value, string propertyName)
    {
        var validationResults = ValidationHelper.ValidateProperty(this, value, propertyName);
        OnValidateProperty<T>(value, propertyName, validationResults);
        if (validationResults.Count > 0)
        {
            AddErrors(propertyName, validationResults);
            RaiseErrorsChanged(propertyName);
        }
        else
        {
            ClearErrors(propertyName);
        }
    }

    protected virtual void OnValidateProperty<T>(T value, string propertyName, List<ValidationResult> validationResults)
    {
    }

    protected void AddErrors(string propertyName, List<ValidationResult> validationResults)
    {
        if (_errors.ContainsKey(propertyName))
        {
            _errors[propertyName] = validationResults;
        }
        else
        {
            _errors.Add(propertyName, validationResults);
        }
    }

    protected void ClearErrors(string propertyName)
    {
        _errors.Remove(propertyName);
    }

    protected virtual int GetValidationDelay(string propertyName)
    {
        var pi = this.GetType().GetProperty(propertyName);
        if (pi != null)
        {
            var delayAttr = pi.GetCustomAttribute<ValidationDelayAttribute>();
            if (delayAttr != null)
                return delayAttr.Delay;
        }

        return 0;
    }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class ValidationDelayAttribute : Attribute
{
    public ValidationDelayAttribute(int delay)
    {
        Delay = delay;
    }

    /// <summary>
    /// Delay in milliseconds
    /// </summary>
    public int Delay { get; }
}

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:7 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
taoricommented, Dec 7, 2015

ok i’ve browsed through the code of ConventionManager.cs and i guess i can see now how to provide a plugin based on that. I’m busy with other things right now but i’d probably work on it on the weekend and nudge you to have a look if i did it as you would have imagined this to be done (If that’s ok with you.) ?

0reactions
nigel-sampsoncommented, Dec 8, 2015

No worries.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using INotifyDataErrorInfo with child objects in model ...
I'm trying to implement INotifyDataErrorInfo and my model has some custom types that require different validation based on their use.
Read more >
INotifyDataErrorInfo implementation for several values ...
I have been trying to implement INotifyDataErrorInfo and everything seems to be working except SaveSettings() and especially bool HasErrors.
Read more >
How to implement validation before setting a property ...
After validation succeeds, you set the private field and notify the changed property for the UI to be updated according to ViewModel's State....
Read more >
INotifyDataErrorInfo in WPF
This article explains multiple validations on the same property in WPF using INotifyDataErrorInfo and Data annotation.
Read more >
Implementing INotifyDataErrorInfo (WPF) : r/csharp
Personally I am a fan of form validation on user click and not on property changed. I hope I described my ask clearly...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found