Создание ViewModels на основе вложенных объектов модели в WPF и шаблоне MVVM
У меня возникла проблема с пониманием того, как создавать модели просмотра на основе следующих моделей.
(я упростил модели, чтобы быть более четкими)
public class Hit
{
public bool On { get; set;}
public Track Track { get; set; }
}
public class Track
{
public ObservableCollection<Hit> Hits { get; set; }
public LinearGradientBrush Color { get; set; }
public Pattern Pattern { get; set; }
}
public class Pattern
{
public string Name { get; set; }
public ObservableCollection<Tracks> Tracks { get; set; }
}
Теперь, моя проблема в том, как создать ViewModels..
Мне нужно сохранить исходные отношения через модели, beacaus У меня есть метод Serialize() на шаблоне, который сериализует его в XML файл.. (с соответствующими треками и хитами)
Чтобы иметь возможность привязывать шаблон к пользовательским элементам управления, и вложенные шаблоны, я должен также иметь шаблон PatternViewModel с ObservableCollection <TrackViewModel> в нем, то же самое для TrackViewModel и HitViewModel.. и я neet для создания пользовательских свойств представления на моделях представления, которые не являются частью бизнес-объекта (цвета и т.д.).
Мне кажется, что не очень хорошо дублировать все отношения моделей на моделях просмотра...
и отслеживание всех этих отношений при кодировании моделей просмотра также гораздо более подвержено ошибкам.
у кого есть лучший подход/решение?
Ответы
Ответ 1
В итоге я использовал часть решения, предложенное Джо Уайтом, слегка отличающимся образом
Решение заключалось в том, чтобы просто оставить модели такими, какими они были в начале, и привязать к коллекциям обработчик событий для CollectionChanged из внутренних коллекций, например, PatternViewModel будет выглядеть следующим образом:
public class PatternViewModel : ISerializable
{
public Pattern Pattern { get; set; }
public ObservableCollection<TrackViewModel> Tracks { get; set; }
public PatternViewModel(string name)
{
Pattern = new Pattern(name);
Tracks = new ObservableCollection<TrackViewModel>();
Pattern.Tracks.CollectionChanged += new NotifyCollectionChangedEventHandler(Tracks_CollectionChanged);
}
void Tracks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Track track in e.NewItems)
{
var position = Pattern.Tracks.IndexOf((Track) e.NewItems[0]);
Tracks.Insert(position,new TrackViewModel(track, this));
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Track track in e.OldItems)
Tracks.Remove(Tracks.First(t => t.Track == track));
break;
case NotifyCollectionChangedAction.Move:
for (int k = 0; k < e.NewItems.Count; k++)
{
var oldPosition = Tracks.IndexOf(Tracks.First(t => t.Track == e.OldItems[k]));
var newPosition = Pattern.Tracks.IndexOf((Track) e.NewItems[k]);
Tracks.Move(oldPosition, newPosition);
}
break;
}
}
}
Итак, я могу прикрепить новую команду Color/Style/Command на моделях просмотра, чтобы мои базовые модели были чистыми
И всякий раз, когда я добавляю/удаляю/перемещаю элементы в коллекции базовых моделей, коллекции моделей представлений остаются в синхронизации друг с другом
К счастью, мне не нужно управлять множеством объектов в моем приложении, поэтому дублированные данные и производительность не будут проблемой
Мне не нравится это слишком много, но он работает хорошо, и это не очень много работы, просто обработчик событий для модели представления, содержащий другие, рассматривающий коллекции моделей (в моем случае один для PatternViewModel для синхронизации TrackViewModels, а другой - в TrackViewModel для управления HitViewModels)
Все еще интересуются вашими идеями или лучшими идеями =)
Ответ 2
Одна вещь, которую я сделал с некоторым успехом, - это перемещение ObservableCollection из модели. Вот моя общая схема:
- В объектах модели выведите свойство типа
IEnumerable<TModel>
, которое дает доступ к коллекции только для чтения. Используйте обычный старый List<TModel>
, а не ObservableCollection, как коллекцию поддержки.
- Для кода, который необходимо изменить коллекции моделей (добавить, удалить и т.д.), добавить методы к объекту модели. У вас нет внешнего кода, непосредственно управляющего коллекцией; инкапсулировать, что внутри методов на модели.
- Добавить события в модель для каждого типа изменений, которые вы разрешаете. Например, если ваша модель поддерживает только добавление элементов в конец коллекции и удаление элементов, вам потребуется событие ItemAdded и событие ItemDeleted. Создайте потомку EventArgs, который дает информацию об элементе, который был добавлен. Огоньте эти события из методов мутации.
- В вашей модели ViewModel укажите
ObservableCollection<TNestedViewModel>
.
- Примените ViewModel к событиям на модели. Всякий раз, когда модель говорит, что элемент был добавлен, создайте экземпляр ViewModel и добавьте его в ViewModel ObservableCollection. Всякий раз, когда модель говорит, что элемент был удален, повторите команду ObservableCollection, найдите соответствующую ViewModel и удалите ее.
- Помимо обработчиков событий, убедитесь, что весь код мутации коллекции выполняется с помощью модели - рассматривайте ViewModel ObservableCollection как строго что-то для потребления вида, а не то, что вы используете в коде.
Это делает много дублирующихся кодов для каждого другого ViewModel, но это лучшее, что я смог придумать. Он, по крайней мере, масштабируется в зависимости от сложности, которую вам нужно - если у вас есть коллекция, которая добавляется только вам, вам не нужно писать много кода; если у вас есть коллекция, которая поддерживает произвольное переупорядочение, вставки, сортировку и т.д., это намного больше работает.
Ответ 3
Я думаю, что у меня была такая же проблема, и если вы сделаете это как "PatternViewModel с ObservableCollection <TrackViewModel> ", вы также получите огромное влияние на вашу производительность, потому что вы начинаете дублировать данные.
Мой подход заключался в том, чтобы построить - для вашего примера - шаблон PatternViewModel с ObservableCollection <Track> . Это не противоречит MVVM, потому что представление привязано к коллекции.
Таким образом, вы можете избежать дублирования отношений.
Ответ 4
Одно из решений, которое я рассматривал, хотя я не уверен, что он будет работать на практике, - это использовать конвертеры для создания модели просмотра вокруг вашей модели.
Итак, в вашем случае вы можете привязать Tracks
непосредственно к (в качестве примера) списку с конвертером, который создает новый TrackViewModel
из трека. Все, что ваш контроль когда-либо видел, - это объект TrackViewModel
, и все ваши модели когда-либо будут видеть другие модели.
Я не уверен в динамическом обновлении этой идеи, хотя я еще не пробовал ее.