-1

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

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

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

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

纠正这个问题的最佳解决方案是什么?

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

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

4

2 回答 2

1

您应该直接在 Car 类本身中执行验证,或者公开包装器对象,而不是将“真实” 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 类的任何属性,则您实际上是绕过了在 Car 类之外指定的任何验证。

于 2016-12-14T11:24:34.050 回答
0

谢谢mm8的回答,

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

您对此解决方案有何看法:

  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];
       }
 }

你认为这是一个好习惯吗?

于 2016-12-14T17:35:37.317 回答