- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
常见场景:具有项目模型集合的模型。
例如,一栋房子里有一群人。
如何为 MVVM 正确构建此结构 - 特别是通过添加和删除来更新 Model 和 ViewModel 集合?
型号House
包含模型集合People
(通常是 List<People>
)。
查看型号HouseVM
包含它包装的 House 对象和 View 模型的 ObservableCollection PeopleVM
(ObservableCollection<PeopleVM>
)。请注意,我们最终会看到 HouseVM 持有两个集合(需要同步):
1.HouseVM.House.List<People>
2.HouseVM.ObservableCollection<PeopleVM>
当房屋更新为新人员(添加)或人员离开(删除)时,现在必须在模型房屋人员集合和 VM HouseVM PeopleVM ObservableCollection 两个集合中处理该事件。
这个MVVM结构正确吗?
是否有办法避免对添加和删除进行双重更新?
最佳答案
您的一般方法是非常好的 MVVM,让一个 ViewModel 公开其他 ViewModel 的集合是一个非常常见的场景,我到处都在使用它。我不建议直接在 ViewModel 中公开项目,就像 nicodemus13 所说的那样,因为您最终会将 View 绑定(bind)到模型,而在集合项目之间没有 ViewModel。所以,第一个问题的答案是:是的,这是有效的 MVVM。
您在第二个问题中要解决的问题是房屋模型中的人员模型列表与房屋 ViewModel 中的人员 ViewModel 列表之间的同步。您必须手动执行此操作。所以,没有办法避免这种情况。
您可以做什么:实现自定义 ObservableCollection<T>
, ViewModelCollection<T>
,它将其更改推送到底层集合。要获得双向同步,请将模型的集合也设置为 ObservableCollection<> 并注册到 CollectionChanged
ViewModelCollection 中的事件。
这是我的实现。它使用 ViewModelFactory 服务等,但只看一下一般原理。我希望它有帮助...
/// <summary>
/// Observable collection of ViewModels that pushes changes to a related collection of models
/// </summary>
/// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
/// <typeparam name="TModel">Type of models in underlying collection</typeparam>
public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
where TViewModel : class, IViewModel
where TModel : class
{
private readonly object _context;
private readonly ICollection<TModel> _models;
private bool _synchDisabled;
private readonly IViewModelProvider _viewModelProvider;
/// <summary>
/// Constructor
/// </summary>
/// <param name="models">List of models to synch with</param>
/// <param name="viewModelProvider"></param>
/// <param name="context"></param>
/// <param name="autoFetch">
/// Determines whether the collection of ViewModels should be
/// fetched from the model collection on construction
/// </param>
public VmCollection(ICollection<TModel> models, IViewModelProvider viewModelProvider, object context = null, bool autoFetch = true)
{
_models = models;
_context = context;
_viewModelProvider = viewModelProvider;
// Register change handling for synchronization
// from ViewModels to Models
CollectionChanged += ViewModelCollectionChanged;
// If model collection is observable register change
// handling for synchronization from Models to ViewModels
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
// Fecth ViewModels
if (autoFetch) FetchFromModels();
}
/// <summary>
/// CollectionChanged event of the ViewModelCollection
/// </summary>
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
/// <summary>
/// Load VM collection from model collection
/// </summary>
public void FetchFromModels()
{
// Deactivate change pushing
_synchDisabled = true;
// Clear collection
Clear();
// Create and add new VM for each model
foreach (var model in _models)
AddForModel(model);
// Reactivate change pushing
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Return if synchronization is internally disabled
if (_synchDisabled) return;
// Disable synchronization
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Remove(m);
break;
case NotifyCollectionChangedAction.Reset:
_models.Clear();
foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
_models.Add(m);
break;
}
//Enable synchronization
_synchDisabled = false;
}
private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
_synchDisabled = true;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var m in e.NewItems.OfType<TModel>())
this.AddIfNotNull(CreateViewModel(m));
break;
case NotifyCollectionChangedAction.Remove:
foreach (var m in e.OldItems.OfType<TModel>())
this.RemoveIfContains(GetViewModelOfModel(m));
break;
case NotifyCollectionChangedAction.Reset:
Clear();
FetchFromModels();
break;
}
_synchDisabled = false;
}
private TViewModel CreateViewModel(TModel model)
{
return _viewModelProvider.GetFor<TViewModel>(model, _context);
}
private TViewModel GetViewModelOfModel(TModel model)
{
return Items.OfType<IViewModel<TModel>>().FirstOrDefault(v => v.IsViewModelOf(model)) as TViewModel;
}
/// <summary>
/// Adds a new ViewModel for the specified Model instance
/// </summary>
/// <param name="model">Model to create ViewModel for</param>
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
/// <summary>
/// Adds a new ViewModel with a new model instance of the specified type,
/// which is the ModelType or derived from the Model type
/// </summary>
/// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}
关于wpf - MVVM 和 VM 集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15830008/
编辑:我似乎问错了这个问题。 我正在尝试寻找一种方法来查询一个集合是否在另一个集合中可用。例如: SELECT * FROM something WHERE (1, 3) IN (1, 2, 3, 4
这两种方法似乎 produce the same results ,但我一直很难真正说服人们第二种方法有效,因为它显然并不为人所知。 // Create some data var foo = { '
我一直在学习Kotlin,并且遇到过Collections API。在Kotlin之前,我一直在学习Java,并且我知道Java中有很多不同类型的Collections API。例如,我们使用List
为什么我会得到不同的行为: Collection col2 = new ArrayList(col); 集合 col2 = new ArrayList(); col2.addAll(col) 我正在与
所以我有一个代表专辑信息的 JSON 对象。给定“function updateRecords(id, prop, value)”我希望能够更新每个条目。正确的完成代码如下。 我得到了指示,粗体部分,
我想存储一个对象集合,这些对象根据它们所代表的值进行键控。这些键可以重复。例如: [4] => Bob [5] => Mary [5] => Sue [9] => Steve [10] =>
在检查 ArrayList API 时,我注意到一些看起来很奇怪的东西。 确实,这里是 ArrayList 构造函数实现,其中 Collection 作为参数传递: public ArrayList(
我正在为 API 编写一个 swagger 定义文件。 API 是用于 GET 请求的 /path/to/my/api: get: summary: My Custom API d
我知道scala.collection包中有两个非常有用的对象,可以帮助我们实现这个目标: JavaConverters(如果我想明确说明并准确说明我要转换的内容) JavaConversions(如
我已经阅读了无数其他帖子,但似乎无法弄清楚发生了什么,所以是时候寻求帮助了。 我正在尝试将包含集合的域实体映射到也包含集合的 dtos。 这是一个原始示例; (我提前为代码墙道歉,我尽量保持简短):
我正在创建一个具有 ArrayList 的类,因此当我调用构造函数时,它会初始化该数组: public class ElementsList { private ArrayList list;
我正在阅读事件指南和指南的开头,它说: You can also add an event listener to any element in the this.$ collection using
我是 Python 新手,想知道如何使用键在字典中存储不同数据类型的列表 例如 - {[Key1,int1,int1,String1] , [Key2,int2,int2,String2], [Key
int[] mylist = { 2, 4, 5 }; IEnumerable list1 = mylist; list1.ToList().Add(1); // why 1 does not get
我在 UI 表单中的每一行之后将以下内容添加到 HashMap 集合中 声明 Map> map = new HashMap>(); List valSetOne = new ArrayList();
我正在开发我的第一个 Java 项目,我有一个问题。问题应该很简单(虽然代码不是那么短,但没有理由被吓倒:))。我创建了一个基本的角色扮演游戏,并且有一个定义每个角色的抽象类“Character”。在
我正在开发一款应用程序,可以为用户收集推文、Facebook 状态和 Facebook 照片。目前,用户确切地设定了他们希望这种收获发生的时间和时间,并且蜘蛛会在此期间拉取数据。 when 和 to
有谁知道在 C# 中是否有与 Java 的 Set 集合等效的好方法?我知道您可以通过填充但忽略值来使用 Dictionary 或 HashTable 在某种程度上模仿集合,但这不是一种非常优雅的方式
EXISTS 该函数返回 集合中第一个元素的索引,如果集合为空,返回NULLNULLNULL Collecti
RDF集合是通过属性 rdf:parseType="Collection" 来描述仅包含指定成员的组 rdf:parseType="Collection" 属
我是一名优秀的程序员,十分优秀!