Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

INotifyDataErrorInfo and asynchronous data validation in WPF 4.5

Is it allowed to fire ErrorsChanged event from a non-UI thread? I'm looking at the following article:

Validating Data in WPF 4.5 Using the INotifyErrorDataError Interface.

Particularly, I have a question about this code fragment:

private async void ValidateUsername(string username)
{
    const string  propertyKey = "Username";
    ICollection<string> validationErrors = null;
    /* Call service asynchronously */
    bool isValid = await Task<bool>.Run(() => 
    { 
        return _service.ValidateUsername(username, out validationErrors); 
    })
    .ConfigureAwait(false);

    if (!isValid)
    {
        /* Update the collection in the dictionary returned by the GetErrors method */
        _validationErrors[propertyKey] = validationErrors;

        /* Raise event to tell WPF to execute the GetErrors method */
        RaiseErrorsChanged(propertyKey);
    }
    else if(_validationErrors.ContainsKey(propertyKey))
    {
        /* Remove all errors for this property */
        _validationErrors.Remove(propertyKey);

        /* Raise event to tell WPF to execute the GetErrors method */
        RaiseErrorsChanged(propertyKey);
    }
} 

Note how ConfigureAwait(false) is used to allow continuation on a pool thread after await Task<bool>.Run:

This most likely will lead to ErrorsChanged event being fired on a non-UI thread. Which is in contrary to MSDN:

The implementing class should raise this event on the user interface thread whenever the GetErrors return value changes, even if the return value implements INotifyCollectionChanged.

The article seems to have come from a credible source, apparently the code has been tested.

Am I missing something? Is it a bug, or does WPF 4.5 account for this, similar to PropertyChanged?

like image 273
noseratio Avatar asked Feb 05 '14 04:02

noseratio


1 Answers

I would consider this a bug. Then again, I always raise PropertyChanged on the UI thread, too; because even though WPF happens to handle it, other MVVM frameworks may not.

Of course, ideally the service would be asynchronous (since it's I/O-bound), and in that case there would be no need for Task.Run either. Oh, and the sample currently uses an async void method, which will raise an application-level error if anything untoward happens (e.g., if the validation service is unavailable).

Also, anytime you do something asynchronous with the user's input, you really need to think through the user experience regarding delays and errors. In particular, I'd prefer a solution that displayed an inline busy indicator or something so the user knows the field is being validated. Then it can change to a green check or red x or something when the validation completes.

like image 56
Stephen Cleary Avatar answered Oct 29 '22 01:10

Stephen Cleary