您的一般方法是完全正确的MVVM,拥有一个公开其他ViewModel集合的ViewModel是非常常见的情况,我在很多地方都使用它。像nicodemus13所说的那样,我不建议直接在ViewModel中公开项,因为这样你的视图会绑定到没有ViewModel的模型,而在你的集合项之间没有ViewModel。所以,对于你的第一个问题的答案是:是的,这是有效的MVVM。
你在第二个问题中解决的问题是房子模型中的人员模型列表和房屋ViewModel中的人员ViewModel列表之间的同步。你必须手动完成这个过程。所以,没有办法避免这个问题。
![enter image description here](https://istack.dev59.com/29Z8S.webp)
你可以这样做:实现一个自定义的ObservableCollection<T>,ViewModelCollection<T>,将其更改推送到底层集合。为了实现双向同步,使模型的集合也成为ObservableCollection<>,并在ViewModelCollection中注册CollectionChanged事件。
这是我的实现。它使用一个ViewModelFactory服务等等,但只是看一下一般原则就行了。希望能有所帮助...
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;
public VmCollection(ICollection<TModel> models, IViewModelProvider viewModelProvider, object context = null, bool autoFetch = true)
{
_models = models;
_context = context;
_viewModelProvider = viewModelProvider;
CollectionChanged += ViewModelCollectionChanged;
if (models is ObservableCollection<TModel>)
{
var observableModels = models as ObservableCollection<TModel>;
observableModels.CollectionChanged += ModelCollectionChanged;
}
if (autoFetch) FetchFromModels();
}
public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
public void FetchFromModels()
{
_synchDisabled = true;
Clear();
foreach (var model in _models)
AddForModel(model);
_synchDisabled = false;
}
private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_synchDisabled) return;
_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;
}
_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;
}
public void AddForModel(TModel model)
{
Add(CreateViewModel(model));
}
public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
{
var m = new TSpecificModel();
Add(CreateViewModel(m));
}
}