MVVM & 业务逻辑层

MVVM & business logic Layer

我对 MVVM 模式和绑定集合有疑问。我的 ViewModel 为 View 提供了一个集合,但是为了获得这个集合,我使用了这个:

public BindingList<Car> BindingListCars { get; set; }

public CarsVm()
{
    BindingListVoiture = carServices.ListCars;
}

当我在这个列表上绑定我的视图时,就好像我直接在模型上绑定我的视图,因为它们使用相同的引用。所以当我编辑一个Car中的一个属性时,直接编辑了模型,没有使用carServices验证方法。

解决此问题的最佳解决方案是什么?

我是否必须向我的视图公开我的模型副本才能不直接从视图编辑我的模型?

我是否必须在我的模型中使用 BindingList 并在我的 carServices 中订阅 ListChanged 来验证每个更改?

您应该直接在 Car class 本身中执行验证,或者公开包装器对象而不是将 "real" Car 对象公开给视图。

下面的示例代码应该让您明白我的意思:

//the "pure" model class:
public class Car
{
    public string Model { get; set; }
}


public class CarService
{
    public List<CarWrapper> ListCar()
    {
        List<Car> cars = new List<Car>(); //get your Car objects...

        return cars.Select(c => new CarWrapper(c, this)).ToList();
    }

    public bool Validate()
    {
        //
        return true;
    }
}

public class CarWrapper
{
    private readonly Car _model;
    CarService _service;
    public CarWrapper(Car model, CarService service)
    {
        _model = model;
        _service = service;
    }

    //create a wrapper property for each property of the  Car model:
    public string Model
    {
        get { return _model.Model; }
        set
        {
            if(_service.Validate())
                _model.Model = value;
        }
    }
}

显然,如果您从视图模型中公开一个 IEnumerable 以供视图绑定,那么如果视图能够设置任何在 Car 外部定义的验证,那么您将有效地绕过任何在 Car 外部定义的验证 class汽车的属性 class.

谢谢mm8的回答,

使用此解决方案,我必须为每个 class 创建一个需要外部验证的包装器。它增加了工作量,并且在重构期间我们必须编辑 class 和包装器。

您如何看待这个解决方案:

  1. 我将我的车辆清单放入绑定列表
  2. 我的服务订阅了这个列表的 ListChanged 事件
  3. 我的服务实现 INotifyDataErrorInfo
  4. 对于此列表中的每个修改都会执行验证
  5. 如果出现错误,则引发 ErrorsChanged 事件 视图模型订阅此事件并检索错误数据。
  6. 视图模型订阅此事件并检索错误数据。

例如:

我的服务实现:

public class VehicleServices : INotifyDataErrorInfo
{

     private BindingList<Vehicle> _bindingListCar
     public BindingList<Vehicle> BindingListCar 
     { 
         get return _bindingListCar;
     }

     private readonly Dictionary<string, ICollection<string>>
        _validationErrors = new Dictionary<string, ICollection<string>>();

     //INotifyDataErrorInfo implementation

     public IEnumerable GetErrors(string propertyName)
     public bool HasErrors
     private void RaiseErrorsChanged(string propertyName)

     public VehicleServices()
     {
         _bindingListCar = GetVehicles();
         _bindingListCar.ListChanged += BindingListVehicleChanged;
     }

     private void BindingListVehicleChanged(object sender, ListChangedEventArgs e)
     {
         //Only modification is managed
         if (e.ListChangedType != ListChangedType.ItemChanged) return;
         switch(e.PropertyDescriptor.Name)

         //Validate each property

         //if there is ErrorsChanged is raised
     }
 }

还有我的 ViewModel

 public class CarVm : BindableBase
 {

      private ICollection<string> _errors;

      public ICollection<string> Error
      {
         get
         {
             return _errors;
         }
         set
         {
             SetProperty(ref _errors, value);
         }
      }
      private VehicleServices _carServices;

      public BindingList<Vehicle> BindingListCar { get; set; }

      public CarVm(VehicleServices carServices)
      {
           _carServices = carServices;
           BindingListCar = new BindingList<Vehicle>(_carServices.BindingListCar);
           _carServices.ErrorsChanged += _carServices_ErrorsChanged;
       }

       private void _carServices_ErrorsChanged(object sender, DataErrorsChangedEventArgs e)
       {
           Error = _carServices.ValidationErrors[e.PropertyName];
       }
 }

您认为这是一个好的做法吗?