0

Есть модель пользователя, в которой находится список сообщений этого пользователя.

class User
{
   public int Id {get; set;}
   public List<string> Messages {get;set;}
}

В интерфейсе мы можем просматривать, редактировать и удалять эти сообщения.

Сейчас в UsersVM я добавил ObservableCollection<string> Messages и когда срабатывает команда редактирования или удаления, то фиксирую эти изменения как в UsersVM.Messages, так и в модели User.Messages, что не очень правильно, как мне кажется.

Подскажите, как сделать правильнее?

trydex
  • 3,742
  • А почему вам кажется, что это не очень правильно? – VladD Jan 02 '17 at 06:35
  • @VladD, то, что модель реализует INotifyCollectionChanged, полагаю –  Jan 02 '17 at 09:23
  • @FoggyFinder: Окей, а что в этом плохого? Модель имеет право делать так, как ей вздумается, на то она и модель. – VladD Jan 02 '17 at 09:51
  • @VladD нарушает классическое MVVM, но сам ничего плохого не вижу в том, что бы не следовать всем требованиям. –  Jan 02 '17 at 10:27
  • @FoggyFinder: Эээ, и в чём же нарушение классического MVVM? – VladD Jan 02 '17 at 10:36
  • @VladD, @FoggyFinder, Нет, модель не реализует интерфейс INotifyCollectionChanged. Модель я оставил такую же, как указал в вопросе. А вот уже во ViewModel я добавил ObservableCollection и привязываюсь к ней. И получается, что данные я добавляю в 2 коллекции вместо одной: и в модель, и в VM. То есть в принципе так делать вполне нормально? – trydex Jan 02 '17 at 11:15
  • 1
    @maxwell: Проблем с этим не вижу. Посмотрите, например, тут: http://ru.stackoverflow.com/a/379331/10105 – VladD Jan 02 '17 at 11:49
  • @VladD скопирую ваш ответ Модель пишется в отрыве от реализации MVVM...Модель не знает ни о VM, ни тем более о View, и тем самым независима и может быть легко использована в другом проекте.. ИзменениеList<_> на ObservableCollection<_> ради обновления интерфейса, на мой взгляд, неправильно. –  Jan 02 '17 at 16:19
  • @FoggyFinder: Ага, понял, модель должна хоть как-то сообщать о своих изменениях. Через INotifyCollectionChanged или как угодно ещё, это её задача по идее. – VladD Jan 02 '17 at 17:06
  • @FoggyFinder: Так что каким бы образом модель не сообщала о своих изменениях, это всё равно правильно. По-моему, так. – VladD Jan 03 '17 at 09:09

1 Answers1

1

Конечно, дублировать данные не рационально. Реализуйте интерфейс INotifyCollectionChanged


На самом деле сделать это может быть не так просто, но, благо, есть доступ к исходным кодам ObservableCollection

Как видно, в основе этого класса лежит Collection<T>:

Класс Collection также имеет конструктор, который принимает существующую реализацию IList. В отличие от других классов коллекций, передаваемый список не копируется, а для него создается прокси, а это значит, что последующие изменения будут отражаться в оболочке Collection (хотя и без запуска виртуальных методов Collection). И наоборот, изменения, внесенные через Collection, будут воздействовать на лежащий в основе список.

Таким образом, мы можем скопировать реализацию ObservableCollection и добавить в нее примерно такой конструктор (заменить существующий(-е)):

public ObservableCollection(IList<T> list) : base(list)
{ }

Это позволит пользоваться уже готовой коллекцией модели, без дублирования и, в то же время, всеми преимуществами ObservableCollection

  • 4
    А можете привести пример для данного случая, как в ответе? Сделайте ваш ответ более информативным. – Vadim Ovchinnikov Jan 02 '17 at 06:31
  • @VadimOvchinnikov, обновил ответ. – Андрей NOP Jan 09 '17 at 16:43
  • 1
    Отлично, а скажите, а не проще ли унаследоваться от ObservableCollection, чем таскать с собой почти полный дубль её исходного кода? – Vadim Ovchinnikov Jan 09 '17 at 16:52
  • @VadimOvchinnikov, к сожалению ваш вариант не сработает, так как Collection.Items не имеет сеттера и единственный вариант присвоить приватному полю items ссылку на свой IList - вызов конструктора Collection(IList<T> list). В дочернем же ObservableCollection функционал переопределен и там используется копирование из одного списка в другой, а не копирование ссылки на список. – Андрей NOP Jan 10 '17 at 09:30
  • Можно унаследоваться и добавить приватный сеттер для Collection.Items, где с помощью рефлексии будут устанавливаться значения приватному полю items. – Vadim Ovchinnikov Jan 10 '17 at 10:31
  • Надеюсь, я верно понимаю вашу логику, потому что кода вы привели только для добавления конструктора. – Vadim Ovchinnikov Jan 10 '17 at 10:32
  • Ну я ничего плохого не вижу в том, чтобы таскать за собой нужный класс в 200 строчек. Вы ведь таскаете за собой, например, mvvm framework или другие полезные наработки. А вот через рефлесию это уже не красиво. Ну тут еще вина разработчиков фреймворка, в том что они скрыли такой полезный функционал, как прокси-коллекция. – Андрей NOP Jan 10 '17 at 15:28