如何在使用集合时同步MVVM中的数据

本文关键字:MVVM 同步 数据 集合 | 更新日期: 2023-09-27 18:01:27

最近我开始深入研究MVVM来构建我正在开发的WPF应用程序。我正在努力理解如何在模型和ViewModel之间保持集合同步,并与之结合,如何验证用户将输入的信息。

假设我有一个(理论)类Building,即模型,它将在运行时将建筑布局存储在内存中,否则将通过序列化存储在xml中。Building有一个成员列表,该列表中的每个入口层都可以有其他列表,比如List和List,这些列表也可以有成员,这些成员是List(即。列表)。

模型:

namespace TestMVVM
{
    public class Building
    {
        public string strName { get; set; }
        public List<Floor> floors { get; set; }
    }
    public class Floor
    {
        public int iNumber { get; set; }
        public List<Room> rooms { get; set; }
    }
    public class Room
    {
        public int iSize { get; set; }
        public string strName { get; set; }
        public List<Door> doors { get; set; }
    }
    public class Door
    {
        public bool bIsLocked { get; set; }
    }
}

在视图中,类型为Floor的List将在DataGrid中可编辑。用户可以在DataGrid中输入新行,以便向Building类添加Floor。在另一个数据网格中,房间可以添加到一个楼层。当我将所有列表都放入observablecollection中,并直接将它们与View耦合时,这非常容易。然而,这也意味着没有适当的关注点分离,一旦验证开始发挥作用,它就会变得混乱。

所以我写了一个ViewModel类,BuildingViewModel。它将保存对模型实例的引用。这就是我遇到麻烦的地方:ViewModel将持有FloorViewModel类型的ObservableCollection。但是,当用户添加条目时,我如何向模型中的List添加条目呢?最重要的是,保持数据同步?如果将一个房间添加到一个楼层,或者将一个门添加到一个房间,如何知道在模型中的哪里更新哪些数据?Ie。如何同步嵌套列表成员数据?

随后,我将确保没有重复的地板可以创建;ie。如果用户添加的楼层带有列表中已经存在的数字,则DataGrid必须报告错误。如果已存在的楼层被编辑,则相同,对于房间名称也是如此。我认为这种错误检查不能在FloorViewModel类中发生,因为它无法访问自身的其他实例。

我已经搜索了很多,但没有找到明确的答案。这似乎是一种相当常见的情况?也许我只是走错了方向?

这是当前的ViewModel,其中ViewModelBase是一个泛型类,包含INotifyProretyChanged和INotifyDataErrorInfo的实现。

namespace TestMVVM
{
    public class BuildingViewModel : ViewModelBase
    {
        private Building building;
        public string strName
        {
            get { return building.strName; }
            set
            {
                building.strName = value;
                if (value == "") AddError("strName", "Name cannot be empty.");
                OnPropertyChanged("strName");
            }
        }
        public ObservableCollection<FloorViewModel> floors
        {
            // what goes here? how to sync members of floor to the model, and validate data?
        }
        public BuildingViewModel(Building b)
        {
            building = b;
        }
    }
    public class FloorViewModel : ViewModelBase
    {
        public ObservableCollection<Room> rooms
        {
            // what goes here? how to sync members of room to the right Floor of the model, and validate data?
        }
    }
    // etc
}

如何在使用集合时同步MVVM中的数据

您提供的类有问题。试着应用得米忒耳定律,观看这个视频,了解如何正确地构建House对象(甚至是同一个例子),然后你只调用正确的关卡addX()方法,这将验证。

看,你需要再读一遍MVVM概念…所有的想法是每个视图都有一个视图模型。在我们的情况下试试:

namespace TestMVVM
{
    public class BuildingViewModel : ViewModelBase
    {
        private Building building;
        private ObservableCollection<Floor> _floors;
        public string strName
        {
            get { return building.strName; }
            set
            {
                //building.strName = value;
                if (String.IsNullOrEmpty(value)) 
                {
                    AddError("strName", "Name cannot be empty.");
                    return;
                 }
                 building.strName = value;
                OnPropertyChanged("strName");
            }
        }
        public ObservableCollection<Floor> floors
        {
            get
            {
               return _floors;
            }
            set 
            {
               _floors = value;
            }
        }
        public BuildingViewModel(Building b)
        {
            building = b;
        }
        public void AddNewFloor(Floor)
       {
          // valid your floor
          // floors.Add(floor);
        }
    }

现在我建议您添加一个函数来验证您在地板上的更改,而不是在属性的setter中。

或者重写/创建ObservableCollection类并重新定义所有方法:

    public class MyObservableCollection<T> : ICollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    public event PropertyChangedEventHandler PropertyChanged;
    public int Count { get { return _reference.Count; } }
    public bool IsReadOnly { get { return _reference.IsReadOnly; } }

    private readonly IList<T> _reference;

    public MyObservableCollection(IList<T> reference)
    {
        _reference = reference;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _reference.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    public void Add(T item)
    {
        _reference.Add(item);
        SendNotification();
    }
    public void Clear()
    {
        _reference.Clear();
        SendNotification();
    }
    public bool Contains(T item)
    {
        return _reference.Contains(item);
    }
    public void CopyTo(T[] array, int arrayIndex)
    {
        _reference.CopyTo(array, arrayIndex);
    }
    public bool Remove(T item)
    {
        var result = _reference.Remove(item);
        SendNotification();
        return result;
    }
    private void SendNotification()
    {
        if (CollectionChanged != null)
        {
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(new NotifyCollectionChangedAction()));
        }
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("..."));
        }
    }
}

为什么不改变Model的type (List to ObservableCollection)

在本例中:

public ObservableCollection<FloorViewModel> floors
    {
        get{return building.floors;}
    }