none
child-level validation in MVVM

    Question

  • Hi all,

    Suppose I have a Master-Child Model like this:

    public class Truck
    {
       public string TruckNo;
       public string DeliveryDate;
       public LineItem[] LineItems;
    }
    
    public class LineItem
    {
       public string ItemNo;
       public string Description;
       public decimal Quantity;
    }
    
    


    As I read from many posts different forums, people say that validations should be done in the ViewModel. So I copied all the fields in Truck classes to ViewModel and implement the validation thru the IDataErrorInfo interface. 

    public class MyViewModel: IDataErrorInfo, INotifyPropertyChanged
    
    {
       private string _truckNo {get; set;}
       public string TruckNo 
       {
           get {return _truckNo;}
           set 
           {
              _truckNo = value;
              OnPropertyChanged("TruckNo");
           }
       }
       
       private string _deliveryDate {get; set;}
       public string DeliveryDate 
       {
           get {return _deliveryDate;}
           set 
           {
              _deliveryDate = value;
              OnPropertyChanged("DeliveryDate");
       }
    
       #region IDataErrorInfo Members
    
      public string Error
      {
        get { throw new NotImplementedException(); }
      }
    
      public string this[string columnName]
      {
        get 
        {
          string result = null;
    
          if( columnName == "TruckNo" )
          {
            if (String.IsNullOrEmpty( TruckNo) )
              result = "Truck No has to be set!";
            else if (TruckNo.Length < 5)
              result = "Truck No has to be at least 5 characters!";
          }
          else if (columnName == "DeliveryDate")
          {
            if (String.IsNullOrEmpty(DeliveryDate))
              result = "Delivery Date has to be set!";
            else if (DeliveryDate.Length > DateTime.Today)
              result = "Delivery date must be greater than system date!";
          }
    
          return result;
        }
      }
    
      #endregion
    
    
    }
    


    My problem is how should I implement the IDataErrorInfo for my LineItem class in that ViewModel? If I need another ViewModel (eg. LineItemViewModel) for my LineItem class, how should I pass the Delivery Date to the LineItemViewModel if its validation requires the Delivery Date? (Better I don't need to include DevlieryDate in my LineItem class)

     

    Thank you in advance!

    Monday, August 8, 2011 9:12 AM

Answers

  • Typically, the model implements the facilities that make it easy to bind to the view. The model supports data validation and error reporting through the IDataErrorInfo (or INotifyDataErrorInfo) interfaces. These interfaces allow WPF and Silverlight data binding to be notified when values change so that the UI can be updated. They also enable support for data validation and error reporting in the UI layer.  The View will report any errors regardless of where the interface is implemented.

    Validation is usually not specific to a view but rather to an object.  For example Person; a Person may have a requirement that their FirstName is required.  This would be enforced in the Person POCO and not the ViewModel.  This requirement is not specific to a View.  This requirement is specific to the object.  Based on your approach you would have to duplicate this logic for every viewModel that uses a Person object.  Not only that, but you are duplicating more than just validation logic. You will recreate an object's property for every ViewModel, just to wrap something you already have access to.  Something to keep in mind is that you typically only have one ViewModel per View.

    Sometimes you will need to work with model objects that do not implement the INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo, or INotifyDataErrorInfo interfaces. In those cases, the ViewModel may need to wrap the model objects and expose the required properties to the View. The values for these properties will be provided directly by the model objects. The ViewModel will implement the required interfaces for the properties it exposes so that the view can easily data bind to them.  This is not a common approach.

    So to summarize; validation belongs on the POCO.

    Tuesday, August 9, 2011 2:22 PM

All replies

  • You are right, you can use a sub viewmodel for LineItem. You can have a field-DeliveryDate in that view model that can be used in indexer while validating another fields of lineitem class.
    Monday, August 8, 2011 9:46 AM
  • That is not the proper method for validation of POCO objects.  Your POCO classes should implement the IDataErrorInfo interface, not the VM.  There is no need for any sub-VMs. Both the Truck and LineItem classes will implement IDataErrorInfo.
    Monday, August 8, 2011 2:02 PM
  • Hi Brian,

    Please correct me if I am wrong, I believe that validation of an POCO's properties should be done at VM level as it is the requirement for that particular view(error has to be shown to a particular view) and not of the model/POCO(business logic will never use IDataErrorInfo's validation if ever try to change some properties of object) as attaching validation to POCO may lead extra burden of validation to the business layer.


    Tuesday, August 9, 2011 11:37 AM
  • Typically, the model implements the facilities that make it easy to bind to the view. The model supports data validation and error reporting through the IDataErrorInfo (or INotifyDataErrorInfo) interfaces. These interfaces allow WPF and Silverlight data binding to be notified when values change so that the UI can be updated. They also enable support for data validation and error reporting in the UI layer.  The View will report any errors regardless of where the interface is implemented.

    Validation is usually not specific to a view but rather to an object.  For example Person; a Person may have a requirement that their FirstName is required.  This would be enforced in the Person POCO and not the ViewModel.  This requirement is not specific to a View.  This requirement is specific to the object.  Based on your approach you would have to duplicate this logic for every viewModel that uses a Person object.  Not only that, but you are duplicating more than just validation logic. You will recreate an object's property for every ViewModel, just to wrap something you already have access to.  Something to keep in mind is that you typically only have one ViewModel per View.

    Sometimes you will need to work with model objects that do not implement the INotifyPropertyChanged, INotifyCollectionChanged, IDataErrorInfo, or INotifyDataErrorInfo interfaces. In those cases, the ViewModel may need to wrap the model objects and expose the required properties to the View. The values for these properties will be provided directly by the model objects. The ViewModel will implement the required interfaces for the properties it exposes so that the view can easily data bind to them.  This is not a common approach.

    So to summarize; validation belongs on the POCO.

    Tuesday, August 9, 2011 2:22 PM
  • Thanks Brian.
    Wednesday, August 10, 2011 11:22 AM
  • Thanks Brian,

     

    If my validation on LineItem class requires the information on Truck levle (eg. the DeliveryDate), how should I get this piece of information if I don't want to include this DeliveryDate field in my LineItem class (becoz it seems non-sense to include delivery date in a line item). 

     

    Thanks again. 

    Thursday, August 11, 2011 3:22 AM
  • A common approach would be to add a property to your LineItems class called Parent that is of type Truck.  Then each time a LineItem is added you simply set the Parent property.  Then you would have access to the LineItems parent Truck data.
    Thursday, August 11, 2011 1:22 PM