Warning: very long and detailed post.
Ok, validation in WPF when using MVVM. I read a lot of things now, looked at many SO questions and tried many approaches, but at some point everything looks a little strange, and I'm really not sure how to do it right β’.
Ideally, I want all validation to happen in the view model using IDataErrorInfo ; so here is what i did. However, there are various aspects that make this decision not a complete solution for the whole topic of verification.
Situation
Let us take the following simple form. As you can see, nothing special. We just have two text fields that bind to string and the int property in each view model. In addition, we have a button associated with ICommand .

So, for verification, we now have two options:
- We can run the check automatically every time the value of the text field changes. Thus, the user receives an instant response when they enter something invalid.
- We can take another step to disable the button when errors occur.
- Or we can run the test only explicitly when the button is pressed, and then all errors are shown, if applicable. Obviously, we cannot disable the button for errors here.
Ideally, I want to implement option 1. For normal data bindings with activated ValidatesOnDataErrors this is the default behavior. Therefore, when the text changes, the binding updates the source and starts checking IDataErrorInfo for this property; errors are reported in reverse. So far, so good.
Validation Status in the View Model
An interesting point is to inform the view model or button in this case of errors. How IDataErrorInfo works, it is mainly designed to report errors back to the view. This way, the view can easily see if there are any errors, display them and even show annotations using Validation.Errors . In addition, validation always occurs by looking at a single property.
Thus, it is difficult to have an idea of ββthe presentation model when there are any errors or if the verification was successful. A common solution is to simply run an IDataErrorInfo check for all properties in the view model itself. This is often done using the separate IsValid property. The advantage is that it can also be easily used to disable a command. The disadvantage is that it can run a check on all properties too often, but most checks should be simple enough so as not to degrade performance. Another solution would be to recall which properties caused errors when using validation, and only validate them, but in most cases this seems too complicated and unnecessary.
The bottom line is that this may work fine. IDataErrorInfo provides verification of all properties, and we can simply use this interface in the view model itself to perform verification there for the entire object. Presenting the problem:
Mandatory Exceptions
The view model uses the actual types for its properties. Thus, in our example, the whole property is the actual int . However, the text box used in the view only supports text. Thus, when binding to int in the view model, the data binding engine will automatically perform type conversions - or at least try. If you can enter text in a text field dedicated to numbers, it is highly likely that there will not always be real numbers inside: therefore, the data binding mechanism will not be able to convert FormatException .

From the viewpoint, we can easily see it. Exceptions from the binding mechanism are automatically caught by WPF and displayed as errors - you donβt even need to include the Binding.ValidatesOnExceptions that are required for the exceptions, Binding.ValidatesOnExceptions in the installer. Error messages have a common text, so this can be a problem. I solved this for myself using the Binding.UpdateSourceExceptionFilter handler, the Binding.UpdateSourceExceptionFilter thrown exception and looking at the source property, and then generating a less general error message. All of this is hidden in my own Binding markup extension, so I can have all the necessary defaults.
So the opinion is in order. The user makes a mistake, sees some kind of mistake and can fix it. The presentation model is however lost. Since the binding engine threw an exception, the source was never updated. Thus, the view model still has the old value, which is not displayed to the user, and the IDataErrorInfo check IDataErrorInfo obviously not applicable.
Even worse, the view model does not have a good way to find out. At least I have not yet found a good solution for this. What would be possible is to return the presentation report to the presentation model indicating that an error has occurred. This can be done by binding the data of the Validation.HasError property to the view model (which is not possible directly), so the view model can first check the state of the views.
Another option is to pass the exception handled by Binding.UpdateSourceExceptionFilter to the view model so that it also Binding.UpdateSourceExceptionFilter about it. A view model can even provide some kind of binding interface to report these things, allowing for custom error messages instead of common to each type of message. But this will create a stronger link between the view and the view model, which I usually want to avoid.
Another βsolutionβ is to get rid of all typed properties, use the properties of a simple string and instead perform the conversion in the view model. This would obviously move the whole validation to the presentation model, but it would also mean an incredible amount of duplication of things, which the data binding mechanism usually takes care of. In addition, this would change the semantics of the view model. For me, the presentation is built for the presentation model, and not vice versa - of course, the design of the presentation model depends on what we represent for the presentation, but there is still general freedom how the presentation does it. Thus, the view model defines the int property because there is a number; Now the view can use a text field (resolving all these problems) or use what works with numbers initially. So no, changing property types to string is not an option for me.
In the end, it is a problem of vision. A view (and its data binding mechanism) is responsible for providing the correct values ββfor the view model to work with. But in this case, there seems to be no good way to tell the view model that it should invalidate the old value of the property.
Bindinggroups
Linking groups is one of the ways I tried to do this. Binding groups have the ability to group all checks, including IDataErrorInfo and IDataErrorInfo exceptions. If they are available for the presentation model, they even have the ability to check the validation status for all of these validation sources, for example, using CommitEdit .
By default, linking groups implement option 2 on top. They explicitly update the bindings, essentially adding an extra uncommitted state. Thus, when a button is pressed, the team can commit these changes, cause updates to the source and all checks and get a single result if successful. Thus, the action of the command may be as follows:
if (bindingGroup.CommitEdit()) SaveEverything();
CommitEdit will return true only if all CommitEdit checks CommitEdit successful. It will take into account IDataErrorInfo and also check for binding exceptions. This seems to be the ideal solution for choice 2. The only thing that causes a lot of trouble is managing a group of bindings using bindings, but I created myself something that basically takes care of this ( connected ).
If a binding group is present for the binding, the default binding will be explicit UpdateSourceTrigger . To implement option 1 from above using binding groups, we basically need to change the trigger. Since I have my own snap extension anyway, it's pretty simple, I just installed it on LostFocus for everyone.
So now the bindings will be updated when the text field changes. If the source can be updated (the binding mechanism does not throw an exception), then IDataErrorInfo will work as usual. If it could not be updated, the view can still see it. And if we press our button, the basic command can call CommitEdit (although nothing needs to be fixed) and get a general check result to see if it can continue.
We may not be able to easily disable the button in this way. At least not from the point of view of the model. Checking the check again and again is not a good idea to simply update the status of the command, and the view model is not notified when in any case a binding mechanism exception is thrown (which should then disable the button), or when it disappears to turn on the button again. We can still add a trigger to disable the button in the view using Validation.HasError so this is not impossible.
Decision?
All in all, this seems to be the perfect solution. What is my problem though? To be honest, I'm not quite sure. Linking groups is a complex thing that seems to be commonly used in small groups, possibly with multiple linking groups in one view. Using one large linking group for the whole presentation just to ensure my validation gives the impression that I am abusing it. And I just keep thinking that there should be a better way to resolve this whole situation, because, of course, I cannot be the only one who has these problems. And so far, I really have not seen many people generally use binding groups to check with MVVM, so this seems weird.
So what exactly is the correct way to check in WPF with MVVM, while checking for binding mechanism exceptions?
My decision (/ hack)
First of all, thanks for your input! As I wrote above, I already use IDataErrorInfo to validate my data, and I personally think that this is the most convenient utility for performing validation. I use utilities similar to what Sheridan suggested in his answer below, so maintenance also works fine.
In the end, my problem boiled down to the problem of mandatory exceptions when the presentation model simply did not know when it happened. Although I could deal with this with the help of the required groups, as described above, I still decided not to do it, because I just felt uneasy about it. So what have I done instead?
As I mentioned above, I detect view-side binding exceptions by listening to UpdateSourceExceptionFilter bindings. There I can get a reference to the view model from DataItem binding DataItem . Then I have an IReceivesBindingErrorInformation interface that registers the view model as a possible recipient of binding error information. Then I use this to pass the binding and exception path to the view model:
object OnUpdateSourceExceptionFilter(object bindExpression, Exception exception) { BindingExpression expr = (bindExpression as BindingExpression); if (expr.DataItem is IReceivesBindingErrorInformation) { ((IReceivesBindingErrorInformation)expr.DataItem).ReceiveBindingErrorInformation(expr.ParentBinding.Path.Path, exception); }
In the view model, I recall whenever I get informed about the path binding expression:
HashSet<string> bindingErrors = new HashSet<string>(); void IReceivesBindingErrorInformation.ReceiveBindingErrorInformation(string path, Exception exception) { bindingErrors.Add(path); }
And whenever the IDataErrorInfo repeatedly IDataErrorInfo property, I know that the binding worked, and I can remove the property from the hash set.
In the view model, I can then check whether the collection of hashes contains any elements, and abort any action that requires a full check of the data. This might not be the best solution because of the relationship between the view and the view model, but using this interface is at least a few less problems.