gpt4 book ai didi

wpf - MVVM 和 VM 集合

转载 作者:行者123 更新时间:2023-12-02 06:29:35 25 4
gpt4 key购买 nike

常见场景:具有项目模型集合的模型。
例如,一栋房子里有一群人。

如何为 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 列表之间的同步。您必须手动执行此操作。所以,没有办法避免这种情况。

enter image description here

您可以做什么:实现自定义 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com