Как создать MVVM с помощью коллекций?
У меня возникли проблемы с пониманием того, как применять шаблон MVVM при включении списков/коллекций.
Скажите, что MainModel имеет несколько свойств и методов, а также список, содержащий другие объекты DetailModel. Объекты DetailModel могут быть добавлены, удалены или переупорядочены.
В MainView будут показаны несколько элементов управления, связанных с корневой моделью, и список ListBox, заполненный из списка. Каждый элемент будет иметь свой собственный суб-просмотр через UserControl DetailModelView.
Наконец, есть MainViewModel. Это свойство обладает свойствами, поддерживаемыми свойствами и методами MainModel, связанными с Main View, с уведомлением об изменении, сохраняющим все в синхронизации. (До этого момента мне нравится шаблон - больше заявляю об этом, если есть что-то фундаментальное, чего я не хватает...)
Когда дело доходит до обработки списка, я запутываюсь. Я столкнулся с несколькими примерами, когда MainViewModel просто предоставляет список DetailModels для представления, а DetailModelViews привязаны непосредственно к моделям. Это работает, но проблематично. Он не последовательно следует шаблону (нет DetailViewModel существует), и он заставляет меня включать некоторый код, связанный с пользовательским интерфейсом, в мои детальные модели. Мне кажется ясным, что MainViewModel должен предоставить список DetailViewModels для пользовательского интерфейса для привязки, но я зациклился на том, как реализовать такую вещь!
Как управлять двумя списками (DetailModels и DetailViewModels)? Я действительно смущен, когда я изначально заполняю список DetailViewModel и как я должен обрабатывать добавление, удаление или изменение порядка элементов, чтобы они синхронизировались!
Ответы
Ответ 1
Обычно Models
- это не что иное, как объекты данных. Они не должны содержать никакого кода, чтобы делать такие вещи, как добавление/удаление элементов из списка. Это задание ViewModel's
.
В вашем случае я создал бы MainViewModel
, который имеет следующие свойства:
-
ObservableCollection<DetailViewModel> Details
-
ICommand AddDetailCommand
-
ICommand RemoveDetailCommand
Если ваш класс MainModel
является объектом данных, вы можете либо его выставить, либо его свойства из MainViewModel
. Выявление этого свойства - это подход "MVVM-пурист", при этом разоблачение всей модели иногда более практично.
Ваш MainViewModel
отвечает за создание начального списка DetailViewModels
, и он отвечает за добавление/удаление этих элементов. Например, в событии PropertyChanged
для свойства MainViewModel.MainModel
он может перестроить коллекцию MainViewModel.Details
, а событие CollectionChanged
для свойства MainViewModel.Details
будет обновлять MainViewModel.MainModel.Details
Ответ 2
Вы имеете право иметь отдельный список DetailModels
и DetailViewModels
. Список DetailViewModels должен быть свойством типа ObservableCollection<DetailViewModel>
. Вы можете заполнить наблюдаемый список при установке модели (или во время построения, если передать модель в конструктор вашего ViewModel.)
private ObservableCollection<DetailViewModel> m_details;
public IEnumerable<DetailViewModel> Details
{
get { return m_details; }
}
Вы можете подписаться на m_details. CollectionChanged. Здесь вы можете обрабатывать переупорядочивание содержимого списка в Модели.
Надеюсь, это поможет.
Ответ 3
По моему опыту, единственный раз, когда вам удается разоблачить объекты модели для представления, - это если вы делаете простое представление только для чтения, например. отображение свойства строки в ComboBox
. Если существует какой-либо фактический пользовательский интерфейс, связанный с объектом (особенно с использованием двусторонней привязки данных), требуется модель представления.
Как правило, главный конструктор VM будет выглядеть следующим образом:
public MasterViewModel(MasterModel m)
{
_Model = m;
_Detail = new ObservableCollection<DetailViewModel>(m.Detail);
}
где MasterModel.Detail
- это набор объектов DetailModel
, а _Detail
- это поле поддержки для свойства Detail
, которое отображается в представлении.
Что касается добавления, удаления и переупорядочения элементов в этом списке, в пользовательском интерфейсе, по крайней мере, это будет выполняться с помощью команд на MasterViewModel
, которые должны обрабатывать как MasterModel.Detail
, так и MasterViewModel.Detail
. Это немного боль, но если вы не хотите повторно заполнять MasterViewModel.Detail
после каждого изменения на MasterModel.Detail
, это действительно неизбежно.
С другой стороны, если вы задавались вопросом "зачем мне когда-либо понадобиться писать модульные тесты для моделей просмотра?", теперь вы знаете.
Ответ 4
Вот ответ, который, я думаю, очень хорошо затрагивает эту проблему, используя ObservableViewModelCollection<TViewModel, TModel>
Это хорошо и лениво. В ctor требуется ObservableCollection и ViewModelFactory. Мне нравится, потому что он сохраняет состояние на уровне модели, где он принадлежит. Пользовательские операции в GUI могут вызывать команды на виртуальной машине, которые манипулируют M посредством общедоступных методов на M. Любые результирующие изменения на уровне M будут автоматически обрабатываться классом в этой ссылке.
fooobar.com/info/125817/...
Обратите внимание на мой комментарий относительно SL vs. WPF