views:

33

answers:

2

I'm looking for a way to validate all the data on a form that has tabs and the tabs contain data templates. I've been looking all over and found a similar question here: http://www.netframeworkdev.com/windows-presentation-foundation-wpf/wpf-validation-data-templates-74176.shtml, but that didn't get a good answer.

Walking the logical tree to check all controls seems to stop at the data template.
Walking the visual tree does not include controls that are not on the currently visible tab.
Using IDataError does not catch invalid input such as letters in a textbox that is bound to a number.

Is there a simple, clean solution to this?

Any help would be much appreciated.

A: 

Have you considered using IDataErrorInfo and using the appropriate bindings to force validation (see here, here, here)?

OJ
OJ, Thanks for your suggestion. IDataErrorInfo has two drawbacks though:1. Invalid input such as letters where there should be numbers is not caught2. The binding must be triggered to get the validation and if the control is in a template and not visible, I don't know a way to trigger the binding...Thanks for the links though!
Frisk
What do you mean when you say invalid input is not caught? That's a function of how you implement validation, not of `IDataErrorInfo`.
Robert Rossney
True, I was assuming the view model was not all strings like the setup in your answer.
Frisk
Frisk, you can fire the validation easily via bindings. You can force validation on exceptions which caters for when users write strings instead of numbers, plus Rob's right, you can implement that how you want. Rob's answer is a more thorough coverage of what I was (for some reason) too lazy to write up myself. +1 Rob!
OJ
+1  A: 

I think the root of your problem is that your view model isn't really a model of the view.

Every property in the view model should be of a type appropriate for the view, not the data model. If you have a numeric property in the data model and you're using a text box in the view to edit it, the property in the view model should be a string.

The view model properties contain the values appearing in the view irrespective of whether or not those values are valid. The only time there should be a discrepancy between the value in the view and the value in the view model is when an editable control has the focus, the user is changing its value, and binding hasn't pushed the new value back to the view model yet.

If you do it this way, I think most of the concerns you've expressed here go away. After you populate a view model object (assuming that you've implemented validation in its setters, and you're not bypassing its setters to update the properties' backing fields directly), its properties are valid or not irrespective of whether or not its view has been instantiated yet.

In my view model classes, I implement a Dictionary<string, string> property named Errors. The validation in the property setters populates Errors with each property's validation error, or null if the property is valid. This makes implementing IDataErrorInfo.Item simple:

public string this[string propertyName]
{
   get
   {
      return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null;
   }
}

I can also implement this property:

public bool IsValid { get { return !(Errors.Values(x => x != null).Any()); } }

which can be used anywhere I need to check to make sure that the view model's current state is valid - say, in the CanExecute property of a Command.

Robert Rossney
Thanks for taking the time to write such a detailed answer. I will give this solution a shot. I was originally planning to use converters for numeric/time values rather than converting them inside my ViewModel classes but I guess this will work as well. Thanks again.
Frisk
There's a place for converters in this pattern, but you have to be discriminating. For instance, a bool-to-Visibility converter fits just fine. It's when converters encounter user input that they can't handle and you need to do anything other than discard it that they break down.
Robert Rossney