views:

32

answers:

1

When creating a custom IValueConverter for a user-editable field, the Convert implementation is usually fairly straightforward.

The ConvertBack implementation is not, since (in the absence of an explicit validation rule) it has to cope with bad user input (eg. typing "hi" in a numeric input field).

If an error occurs during conversion, there doesn't seem to be any way to communicate the specific error:

  • ConvertBack isn't allowed to throw exceptions (if it does, the program terminates).
  • Returning a ValidationError doesn't do anything.
  • Returning DependencyProperty.UnsetValue (which the docs suggest) results in silent failure (no error UI is shown to the user, even if you've set up an error template).
  • Returning the original unconverted value does cause error UI to be shown, but with misleading error text.

Does anyone know of any better way to handle this sort of thing?

(Note: while defining a custom ValidationRule would work, I don't think that's a good answer, since it would basically have to duplicate the conversion logic to discover the error anyway.)

+2  A: 

You can avoid duplicating logic by having the validation rule's first step being to call the value converter to find out if the value it's validating is even usable.

Of course, this couples your validation rules to your value converters, unless you make a validation rule that searches the binding to find out what value converter is in use. But if you start going down that road, sooner later it will probably occur to you, as it has to many, "wait, if I'm using MVVM, what am I doing screwing around with value converters?"

Edit:

If your ViewModel implements IDataErrorInfo, which is really the only way to live, it's relatively straightforward to hook a value converter into a property setter without writing a lot of property-specific validation logic.

In your ViewModel class, create two private fields:

Dictionary<string, string> Errors;
Dictionary<string, IValueConverter>;

Create them (and populate the second) in the constructor. Also, for IDataErrorInfo:

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

Now implement a method like this:

private bool ValidateProperty(string propertyName, Type targetType, object value)
{
   Errors[propertyName] = null;
   if (!Converters.ContainsKey(propertyName))
   {
      return true;
   }
   try
   {
      object result = Converters[propertyName].ConvertBack(value, targetType, null, null)
      return true;
   }
   catch (Exception e)
   {
      Errors[propertyName] = e.Message;
      return false;
   }
}

Now your property setter looks like this:

public SomeType SomeProperty
{
   set
   {
      if (ValidateProperty("SomeProperty", typeof(SomeType), value))
      {
         _SomeProperty = value;
      }
   }
}
Robert Rossney
I'm talking about a situation where I don't want to define a validation rule at all, though.But yes, you're right that you can avoid the problem by eg. defining all user-facing properties in the ViewModel as strings. But that seems a bit extreme too, and *also* doesn't help with showing error UI, at least not with the built-in mechanisms.
Miral
Avoid the problem? Not at all. See my edit. The only thing that the approach I outline messes up is the ability to set the value converter on the binding object. I can't think of any good reason to attach value converters to the bindings if you're using a ViewModel.
Robert Rossney