Недавно мы столкнулись с аналогичной проблемой, но с дополнительным требованием, чтобы Модель состояла из простых глупых POCO.
Наше решение - жестоко применить разделение Model-ViewModel. Ни Модель, ни ViewModel не содержат ObservableCollection<ModelEntity>
, вместо этого Модель содержит коллекцию POCO, а ViewModel содержит ObservableCollection<DetailViewModel>
.
Это легко решает Добавить, Получить и Обновить. Также, если только Мастер удаляет детали из своей коллекции, запускаются соответствующие события.
Однако, если деталь запрашивает удаление, он обязательно должен подать сигнал мастеру (владельцу коллекции).
Это можно сделать, злоупотребив событием PropertyChanged
:
class MasterViewModel {
private MasterModel master;
private ISomeService service;
private ObservableCollection<DetailViewModel> details;
public ObservableCollection<DetailViewModel> Details {
get { return this.details; }
set { return this.details ?? (this.details = LoadDetails()); }
}
public ObservableCollection<DetailViewModel> LoadDetails() {
var details = this.service.GetDetails(master);
var detailVms = details.Select(d =>
{
var vm = new DetailViewModel(service, d) { State = DetailState.Unmodified };
vm.PropertyChanged += this.OnDetailPropertyChanged;
return vm;
});
return new ObservableCollection<DetailViewModel>(detailVms);
}
public void DeleteDetail(DetailViewModel detailVm) {
if(detailVm == null || detailVm.State != DetailState.Deleted || this.details == null) {
return;
}
detailVm.PropertyChanged -= this.OnDetailPropertyChanged;
this.details.Remove(detailVm);
}
private void OnDetailPropertyChanged(object s, PropertyChangedEventArgs a) {
if(a.PropertyName == "State" & (s as DetailViewModel).State == DetailState.Deleted) {
this.DeleteDetail(s as DetailViewModel);
}
}
}
class DetaiViewModel : INotifyPropertyChanged {
public DetailState State { get; private set; } // Notify in setter..
public void Delete() {
this.State = DetailState.Deleted;
}
public enum DetailState { New, Unmodified, Modified, Deleted }
}
Вместо этого вы можете ввести public event Action<DetailViewModel> Delete;
в DetailViewModel
, связать его непосредственно с MasterViewModel::Delete
и т. Д.
Недостатком этого подхода является то, что вам нужно создать множество моделей ViewModel, которые могут никогда не понадобиться для большего, чем их Name, поэтому вам действительно нужно сохранять дешевую конструкцию моделей ViewModel и следить за тем, чтобы список не взрывался.
С другой стороны, вы можете быть уверены, что пользовательский интерфейс привязывается только к объектам ViewModel, и вы можете не допускать большую часть INotifyPropertyChanged goop из вашей модели, обеспечивая вам чистый разрыв между слоями.