Dependency-based validation

Windows Forms has a validation framework built in. WPF defined a new one. Silverlight refined it further. With every platform taking a slightly different approach to validation, it tends to get rather confusing.

Further confusing the issue is the fact that WPF actually has three options:

  • ValidatesOnExceptions
  • IDataErrorInfo (ValidatesOnDataErrors)
  • Data annotations

If you set ValidatesOnExceptions=True within your WPF {Binding}, you will get validation behavior when the setter throws an exception. If you implement IDataErrorInfo in your view model and set ValidatesOnDataErrors=True, then the view model decides when validation errors occur. And if you set data annotations on your view model properties (and put a bit of arcane code in your setters), then the attributes declaratively attach validation rules.

I recommend against ValidatesOnExceptions and data annotations, both for the same reason. If you throw an exception in the setter (which occurs in either option), then the user’s input is kept in the control. It never makes it to the view model where it can be stored. Indeed, the goal is not to store invalid data, but if you don’t store it temporarily, you can’t use it. It just stays in the TextBox or other control until the user fixes it. Your code doesn’t have access to that state, and therefore can’t help the user to resolve the issue. All the while, the control is in a different state than the view model, which makes me very nervous.

So that leaves IDataErrorInfo. In Silverlight, we now have it’s predecessor INotifyDataErrorInfo. Let’s see how to use this interface with Update Controls.

Single responsibility and validation

XAML validation takes place at the DataContext, which in MVVM is the ViewModel. But logically speaking, validation is a domain concept and therefore belongs in the Model. As a responsibility segregation pattern, MVVM is very sensitive to which side of the fence you choose.

Most MVVM implementations put state in the ViewModel. This gives you a place to store user input before it is validated, thus keeping the Model clean. An Update Controls app, however, does not put state in the ViewModel, since other ViewModels could depend upon it. On the plus side, you don't need a message bus to keep ViewModels in sync. On the minus side, you don't have a quarantine area for validation.

The result is that Update Controls guides you to a pattern in which you allow invalid data into the Model, and then present validation errors through the ViewModel. Don’t throw validation errors on input: take the invalid data into the model. Then let the model expose validation through dependent properties. This has the advantage of keeping the presentation responsibilities in the ViewModel, and the domain responsibilities in the Model. It has the disadvantage, however, of letting the Model get dirty. I find the tradeoff acceptable.

public class MyModel
{
    private static Regex ValidPhoneNumber = new Regex(@"\([0-9]{3}\) [0-9]{3}-[0-9]{4}");

    private Independent<string> _phoneNumber = new Independent<string>("");

    public string PhoneNumber
    {
        get { return _phoneNumber; }
        set { _phoneNumber.Value = value; }
    }

    public bool PhoneNumberIsValid
    {
        get { return ValidPhoneNumber.IsMatch(_phoneNumber); }
    }
}

This model has a PhoneNumber property, which can be set to any string. This property is backed by an Independent field so that Update Controls can track dependencies. The PhoneNumberIsValid property depends upon the phone number. When the phone number matches a regular expression, the validation property is true.

Dependent validation

Update Controls is a dependency tracking library. Validation errors displayed to the user are dependent upon the state of the model. One way to express a dependency is with the Dependent<T> field. Let’s set up a dependent boolean based on the phone number validity.

public class MyViewModel : ViewModelBase
{
    private MyModel _model;
    private Dependent<bool> _phoneNumberInvalid;

    public MyViewModel(MyModel model)
    {
        _model = model;

        // Create a dependent boolean that is true when the phone number is invalid.
        _phoneNumberInvalid = new Dependent<bool>(() => !_model.PhoneNumberIsValid);
    }

    public string PhoneNumber
    {
        get { return Get(() => _model.PhoneNumber); }
        set { _model.PhoneNumber = value; }
    }
}

The _phoneNumberInvalid field depends upon the PhoneNumberIsValid model, because that’s what the lambda expression references. When PhoneNumberIsValid changes (based on a change in PhoneNumber), the dependent goes out-of-date. We can hook this event to bring the dependent back up-to-date. We can’t force the update immediately, because things are still changing. Instead, we use the Dispatcher to schedule an update when the current user input event is finished.

// When the dependent goes out-of-date, bring it back up-to-date at the next opportunity.
_phoneNumberInvalid.DependentSentry.Invalidated += delegate
{
    Deployment.Current.Dispatcher.BeginInvoke(delegate
    {
        // Get the new value of the dependent. This brings it up-to-date.
        bool phoneNumberIsInvalid = _phoneNumberInvalid;
    });
};

A Dependent field begins its life out-of-date, so we have to force an update immediately upon construction. We can take this opportunity to save the initial state in a field.

// Get the initial value of the dependent. This brings it up-to-date.
_phoneNumberWasInvalid = _phoneNumberInvalid;

Now we have all the hooks we need to implement INotifyDataErrorInfo. When the phone number validation state changes, we fire the ErrorsChanged event.

// When the dependent goes out-of-date, bring it back up-to-date at the next opportunity.
_phoneNumberInvalid.DependentSentry.Invalidated += delegate
{
    Deployment.Current.Dispatcher.BeginInvoke(delegate
    {
        // Get the new value of the dependent. This brings it up-to-date.
        bool phoneNumberIsInvalid = _phoneNumberInvalid;

        // Fire the event if the status has changed.
        if (phoneNumberIsInvalid != _phoneNumberWasInvalid && ErrorsChanged != null)
            ErrorsChanged(this, new DataErrorsChangedEventArgs("PhoneNumber"));

        _phoneNumberWasInvalid = phoneNumberIsInvalid;
    });
};

We finish the interface out with the GetErrors and HasErrors methods.

public IEnumerable GetErrors(string propertyName)
{
    if (propertyName == "PhoneNumber")
        if (_phoneNumberInvalid)
            yield return "Please enter (###) ###-####";
}

public bool HasErrors
{
    get { return _phoneNumberInvalid; }
}

Validation is the responsibility of the Model. Presentation of errors is a ViewModel responsibility. Like all ViewModel behavior in MVVM, error presentation depends upon the model. We just have to be explicit about it.

From this code, I can see the beginnings of a reusable validation framework within Update Controls. ForView.Wrap could perform all of the steps that we did here. It would just need help determining validation errors for each property. Perhaps a Caliburn-like convention-over-configuration pattern is in order. Please leave a comment if you agree, disagree, or are willing to contribute to the project.

Comments

I like your approach, but ...

... there are a few drawbacks that you did not mention.

You say:
"... logically speaking, validation is a domain concept and therefore belongs in the Model."
"... take the invalid data into the model. Then let the model expose validation through dependent properties. This has the advantage of keeping the presentation responsibilities in the ViewModel, and the domain responsibilities in the Model."

I agree with this because it represents the logical concept of the MVVM model and I would really like to do it that way, but it will not work because not every invalid data will make it's way into the model!
In your example this works because you use a string, but if you have for example a integer instead the user would enter the integer in a Textbox and therefore you have a string in the view and viewmodel but an integer in the model.
If the user now enter say "abc" this cannot be converted into an integer and will therefore not make it's way into the model and can't validate it there!
The result is that you have to do validation in two places, the viewmodel and the model!

There is an article about this issue from Karl Shifflet
https://karlshifflett.wordpress.com/mvvm/input-validation-ui-exceptions-...
but I don't like his solution.

Does anybody know other solutions for this dilemma?

RE: I like your approach, but ...

I overlooked that this matter was addressed already in comments below

Here's to an independent standard for validation, though

In the past I have avoided ever using conventional GUI-specific validation frameworks, because it's not generally obvious how to use them, and I didn't see a clear advantage over manual ad-hoc validation. I would only use a validation "framework" if it were simple, easy to understand, well documented, applicable to a good variety of use cases, and offers a clear advantage over ad-hoc validation. I enjoy the fact that Update Controls is GUI-independent, and to me it feels like the right place to put a minimalist validation framework. - Qwertie

I'm inclined to disagree

While this pattern may be suitable in some cases, I tend to want to keep the model clean, especially if it's something we expect to save to disk. The viewmodel is often stateless under Update Controls, but doesn't have to be; I generally keep a selection flag in the VM, for example, and UC's RecycleBin ensures that the state doesn't get lost. So why not store the invalid data in the VM? It makes sense to me, given that in many/most apps, invalid state should be transient (not saved across sessions). - Qwertie

Domain/storage/navigation/view model

My opinions here are based on a separation of concerns into four areas:

  • Domain model - encodes the rules of the problem domain
  • Storage model - responsible for persistence
  • Navigation model - captures the user's transient point of view
  • View model - projects the domain and navigation models onto the UI

When you speak of keeping the model clean so that it can be saved to disk, you are describing a storage model. There should be no validation in the storage model. It belongs in the domain model.

I prefer to use the Memento pattern to separate the domain model from the storage model. This is not a popular practice, since it means that you have to create duplicate classes. But I find that the extra degree of freedom is often necessary.

Secondly, where you keep a selection flag in the view model, I would move that out to a related navigation model. Sure, this creates more classes, but I often find that two view models need to reference the same navigation model. Without this separation, view models would be communicating with one another. This is why most other MVVM frameworks include a message bus. I prefer to avoid that complexity.

Again, these are my opinions. I understand the reasons for yours, and as you pointed out, UC supports both.

Momentos?

I don't really understand what you mean about momentos--when I looked up the term, I learned that momentos (and/or the Command Pattern) are used for undo/redo stacks. I wonder how you are using them. Do the "domain model" and "storage model" together represent the "model" in MVVM?

Of course, certain kinds of invalid state are not possible to store on disk in the first place. An obvious example would be when a binary float is stored in the disk file, but the user wrote "1.2.3.4" in a text box. So a "storage model" (and the domain model too?) should offer a field of type float, and not the viewmodel data type of string.

I'm not sure what you mean about different VMs communicating either. Some of my VMs have a reference to the PM, but that's not related to selection state, which I have always represented so far as an Independent{bool} in the VM. One VM doesn't care about another VM's selection state.

I haven't used a "message bus" before either, so I can't relate; UC is the first MVVM framework library I have used and I am sufficiently overjoyed with it that I don't expect to try any others :). Indeed, let me say that UC and some of the blog entries here have forever improved the way I do UI development, even if I don't separate concerns in quite the same way you do. - Qwertie

Data type validation

Thanks for your kind words. Your contributions of bug reports, feature requests, and code changes have greatly improved UC. Open source FTW!

Mementos are simple data objects that record the state of another object. Yes, they are sometimes used in undo/redo stacks, to take snapshots of objects as the user changes them. They are also used as data transfer objects in client/server systems, where you want to extract some server-side data and render it into a client-side object.

I like to use them to extract data from my business objects and store them in a database. Then I can load them back in from the database and restore the state of the business objects. This separation between the business objects (a.k.a. domain model) and mementos (a.k.a. storage model) gives me some persistence ignorance that I can use for unit testing, switching to a different back end, or moving back and forth between client and server.

This is antithetical to the Active Record pattern, which puts persistence logic right into the mementos. Just as I separate the mementos from the business objects, I separate the data persistence methods from the mementos. This creates three layers where others might have only one. Hence, this approach is not very popular.

I think your example of the float stored to disk and the string in the view model is a perfect counter-example to the pattern I described. Data type validation should be done as early as possible. If the user input expects a float, then the view model property should be a float, not a string. For that, you have to use ValidatesOnExceptions. The view will get an exception when it tries to set "1.2.3.4" into the view model. The user’s input will never be stored. This behavior may be perfectly acceptable, since the error is confined to a single input.

But if you want to go beyond data type validation and validate inputs against one another, then you need to allow the user’s input through the view model. For these scenarios, I recommend storing invalid data in the model and using dependent validation.