如何在使用集合时同步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
}
您提供的类有问题。试着应用得米忒耳定律,观看这个视频,了解如何正确地构建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;}
}