【问题标题】:How to sync data in MVVM when using collections使用集合时如何在 MVVM 中同步数据
【发布时间】:2015-07-08 08:25:02
【问题描述】:

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

假设我有一个(理论上的)类 Building,即模型,它将在运行时将建筑布局存储在内存中,否则通过序列化存储在 xml 中。 Building 有一个成员 List,并且该列表中的每个条目 Floor 都可以有其他 List,例如 List 和 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 类型的列表可以在 DataGrid 中进行编辑。用户可以在 DataGrid 中输入新行以将 Floor 添加到 Building 类。在另一个 DataGrid 中,可以将房间添加到楼层。当我将所有列表都变成 ObservableCollections 并直接将它们与视图耦合时,这非常容易。但是,这也意味着没有适当的关注点分离,一旦验证开始就会变得混乱。

所以我写了一个 ViewModel 类,BuildingViewModel。它将保存对模型实例的引用。这就是我遇到麻烦的地方:ViewModel 将保存一个 FloorViewModel 类型的 ObservableCollection。但是当用户添加一个条目时,我如何在模型中的列表中也添加一个条目?大多数情况下,保持数据同步?如果将房间添加到楼层,或将门添加到房间怎么办,如何知道模型中的哪个位置更新哪些数据? IE。如何同步嵌套列表成员数据?

随后我会确保不会创建重复的楼层; IE。如果用户使用 List 中已有的数字添加楼层,则 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
}

【问题讨论】:

    标签: c# wpf mvvm datagrid


    【解决方案1】:

    您提供的课程有问题。尝试应用得墨忒耳定律,观看this video 了解如何正确构造 House 对象(甚至是相同的示例),而不是只调用正确级别的addX() 方法,这将验证。

    【讨论】:

    • 感谢@ntohl 提供的链接,这是一个信息量很大的视频,我会按照规定复习我的课程。然后我再看一下如何通过 ViewModel 暴露 Model。我正在努力理解如何在 DataGrid 中将数据更改传播到模型,但我认为我仍然对将 MVVM 模式应用于集合的意义有了进一步的了解......!
    【解决方案2】:

    看你需要再次阅读 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);
            }
        }
    

    现在我建议你添加一个函数来验证你在楼层而不是属性的设置器中所做的更改。

    【讨论】:

      【解决方案3】:

      或者覆盖/创建 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("..."));
              }
          }
      }
      

      【讨论】:

        【解决方案4】:

        为什么不改变 Model 的类型(List to ObservableCollection)?

        在这种情况下:

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

        【讨论】:

        • 感谢您的建议。我认为这相当于直接将模型暴露给视图。此外,例如,所有验证逻辑都将最终出现在模型中。
        猜你喜欢
        • 2010-11-18
        • 1970-01-01
        • 1970-01-01
        • 2017-04-01
        • 1970-01-01
        • 2022-01-18
        • 2020-08-09
        • 2011-09-10
        • 1970-01-01
        相关资源
        最近更新 更多