0

I'm trying to find the best way to validate data in MVVM. Currently, I'm trying to use IDataErrorInfo with Data Annotations using the MVVM pattern.

However, nothing seems to work and I'm not sure what I could be doing wrong. I have something like this.

Model

public class Person : IDataErrorInfo
{
    [Required(ErrorMessage="Please enter your name")]
    public string Name { get; set; }

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return OnValidate(propertyName);
        }
    }

    protected virtual string OnValidate(string propertyName)
    {
        if (string.IsNullOrEmpty(propertyName))
            throw new ArgumentException("Property may not be null or empty", propertyName);

        string error = string.Empty;

        var value = this.GetType().GetProperty(propertyName).GetValue(this, null);
        var results = new List<ValidationResult>();

        var context = new ValidationContext(this, null, null) { MemberName = propertyName };

        var result = Validator.TryValidateProperty(value, context, results);

        if(!result)
        {
            var validationResult = results.First();
            error = validationResult.ErrorMessage;
        }
        return error;
    }
}

Model code courtesy of the solution at How to catch DataAnnotations Validation in MVVM (This answer does not meet my criteria unfortunately.)

ViewModel

public class PersonViewModel
{
    private Person _person;

    public string Name
    {
        get
        {
            return _person.Name
        }
        set
        {
            _person.Name = value;
        }
    }
}


View

<Label Content="Name:" />

<TextBox Text="{Binding UpdateSourceTrigger=LostFocus,
                        Path=Name,
                        ValidatesOnDataErrors=True,
                        NotifyOnValidationError=true}" />


Is there any way to keep the seperation between model, view, and viewmodel while still utilizing data annotations for validation with IDataErrorInfo?

Community
  • 1
  • 1
Charles W
  • 2,262
  • 3
  • 25
  • 38

2 Answers2

6

To keep validation running, IDataErrorInfo must be implemented by the data context, which property is bound to the control. So, it should be something like:

public class PersonViewModel : IDataErrorInfo 
{
    [Required(AllowEmptyStrings = false)]
    public string Name
    {
        get
        {
             return _person.Name
        }
        set
        {
             _person.Name = value;
        }
    }    

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    string IDataErrorInfo.this[string propertyName]
    {
        get
        {
            return OnValidate(propertyName);
        }
    }

    protected virtual string OnValidate(string propertyName)
    {
        /* ... */
    }
}

There's no need to implement IDataErrorInfo in model, this is view model's responsibility. Usually, IDataErrorInfo is implemented by the base class for your view models.

By the way, why OnValidate is protected? How do you imagine overriding of this method?

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • The reason I have the data annotations in the model instead of the viewmodel is because I'm also using Entity Framework to create my database. Entity Framework looks at the Data Annotations in the model to set each columns properties. [Required] == NotNull for example. Is there any way to keep this in the model instead of the viewmodel? – Charles W Mar 12 '14 at 19:45
  • 1
    That's why I don't like EF's data annotations approach. It suits well for MVC, but for MVVM it behaves so confusing... If you still want to use data annotations for EF, you have two ways: clone data annotations in view models, or bind directly to model's properties. Both ways smells for me (although, direct biding to model often convenient). – Dennis Mar 12 '14 at 19:52
0

Keep your definition in XAML notifyonvalidation error and validatesondata errors set to true. And in VM use simple Viewmodel which has validation on setter function for desired properties, if validation fails it should throw validation error and xaml will know that it is invalid.

public class PersonViewModel
{
    private Person _person;

    public string Name
    {
        get
        {
            return _person.Name
        }
        set
        {
            if(value == string.empty)
               throw new ValidationException("Name cannot be empty");
            _person.Name = value;
        }
    }
}

Now you will need to handle this in xaml and display error text from exception

VidasV
  • 4,335
  • 1
  • 28
  • 50
  • 1
    WPF allows several ways to validate data. OP asks about one of them, and this isn't validation, based on exceptions. – Dennis Mar 12 '14 at 19:39