Синхронизация модели двунаправленного представления с «живыми» коллекциями и свойствами - PullRequest
6 голосов
/ 05 февраля 2010

В последнее время у меня возникают проблемы с View Models (VM).

Точно так же, как этот парень Я пришел к выводу, что коллекции, которые мне нужно показать на моей виртуальной машине, обычно имеют тип, отличный от коллекций, представленных на моих бизнес-объектах.

Следовательно, между этими двумя типами должно быть двунаправленное отображение или преобразование. (Просто, чтобы усложнить ситуацию, в моем проекте эти данные являются «живыми», поэтому при изменении свойства они передаются на другие компьютеры)

Я могу почти справиться с этой концепцией, используя такую ​​структуру, как Ферма , хотя я подозреваю, что где-то внутри будет неприятный сюрприз.

Необходимо не только преобразовывать объекты, но и синхронизировать эти две коллекции. (Просто для усложнения вещей я могу вспомнить случаи, когда коллекция виртуальных машин может быть подмножеством или объединением коллекций бизнес-объектов, а не просто синхронизацией 1: 1).

Я вижу, как выполнить одностороннюю «живую» синхронизацию, используя репликацию ObservableCollection или что-то вроде CLINQ.

Тогда возникает проблема: каков наилучший способ создания / удаления элементов?

Двунаправленная синхронизация, кажется, не на картах - я не нашел таких примеров, и единственный класс, который поддерживает что-либо удаленно, это ListCollectionView. Будет ли двунаправленная синхронизация разумным способом добавить обратно в коллекцию бизнес-объектов?

Кажется, что все образцы, которые я видел, никогда не касались этого "комплекса".

Итак, мой вопрос: как вы решаете это? Есть ли какая-нибудь техника для обновления коллекций моделей с ВМ? Каков наилучший общий подход к этому?

Ответы [ 5 ]

4 голосов
/ 05 февраля 2010

Лично я использую ObservableCollection в моей модели и моей модели представления.

class Model
{
    public ObservableCollection<Foo> Foos;
}

class ViewModel
{
    public Model Model;
    public ObservableCollection<FooView> Foos;

    public ViewModel()
    {
        Model.Foos.CollectionChanged += OnModelFoosCollection_CollectionChanged;
    }

    void OnModelFoosCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
       Foo re;

       switch (e.Action)
       {
          case NotifyCollectionChangedAction.Add:
             re = e.NewItems[0] as Foo;
             if (re != null)
                AddFoo(re);  //For other logic that may need to be applied
             break;
          case NotifyCollectionChangedAction.Remove:
             re = e.OldItems[0] as Foo;
             if (re != null)
                RemoveFoo(re); 
             break;
          case NotifyCollectionChangedAction.Reset:
             Foos.Clear();
             /* I have an AddRange in an ObservableCollection-derived class
                You could do Model.Foo.ForEach(ree => AddFoo(ree));
             */
             var converter = 
                from ree in Model.Foo
                select new FooView(ree);
             Reports.AddRange(converter); 
             break;
          default:
             //exercise for the reader :)
             s_ILog.Error("OnModelFoosCollection_CollectionChangedDid not deal with " + e.Action.ToString()); 
             break;
       }
    }   

    void AddFoo(Foo f)
    {
        Foos.Add(new FooView(f));
    }

    void RemoveFoo(Foo f)
    {
       var match = from f in Foos
          where f.Model == f  //if you have a unique id, that might be a faster comparison
          select f;
       if(match.Any())
          Foos.Remove(match.First());
    }
}

Теперь, когда вы удаляете что-то из коллекции Foo модели, она автоматически удаляет соответствующий FooView. Это соответствует тому, как я думаю о подобных вещах. Если я хочу что-то удалить, Модель действительно должна быть удалена.

Кажется, что кода много, но на самом деле не так уж много. Я уверен, что можно создать общую версию этого, но в IMO вы всегда будете нуждаться в собственной логике, связанной с добавлением / удалением элементов.

3 голосов
/ 10 февраля 2010

Я тоже борюсь с двунаправленной синхронизацией двух коллекций для использования с WPF через MVVM. Я в блоге MVVM: обернуть или не обернуть? Сколько ViewModel должен обернуть модель? (Часть 1) и MVVM: обернуть или не обернуть? Должны ли ViewModels переносить коллекции? (Часть 2) относительно вопроса, включая пример кода, который показывает двустороннюю синхронизацию. Однако, как отмечается в постах, реализация не идеальна. Я бы назвал это доказательством концепции.

Мне нравятся платформы BLINQ , CLINQ и Obtics , о которых Alex_P опубликовал. Это очень хороший способ получить одну сторону синхронизации бевайора. Может быть, другая сторона (от ВМ до модели) может быть реализована по альтернативному пути? Я только что опубликовал часть 3 в своем блоге, где обсуждаются некоторые из них.

Из того, что я вижу, двунаправленное через BLINQ и CLINQ не поддерживается в случаях, когда оператор LINQ проецирует данные в новую структуру.

Однако, похоже, что CLINQ может поддерживать двунаправленную синхронизацию в тех случаях, когда запрос LINQ возвращает тот же тип данных, что и базовая коллекция. Это скорее сценарий фильтрации, который не соответствует сценарию использования ViewModel, оборачивающему данные в модели.

3 голосов
/ 08 февраля 2010

Единственная ситуация, когда вам может понадобиться двусторонняя синхронизация, - это когда элемент управления, который вы используете для визуализации своей коллекции виртуальных машин, не сообщает вам о намерении пользователя создать или удалить элемент. То есть контроль имеет дело непосредственно с вашей коллекцией виртуальных машин, и единственный способ узнать, что элемент был добавлен / удален, - это мониторинг коллекции виртуальных машин. Если это не так, вы можете реализовать одностороннюю синхронизацию и добавлять / удалять элементы непосредственно в коллекции модели.

РЕДАКТИРОВАТЬ: возьмите, например, WPF DataGrid контроль связан с наблюдаемой коллекцией ItemViewModels. Если это Свойство CanUserAddRows имеет значение true и пользователь начинает вводить в пустая строка внизу, DataGrid будет использовать конструктор по умолчанию вашего ItemViewModel для создания свободного элемента а затем добавит его в коллекция. Нет никаких указаний от DG, который хочет добавить элемент collection.c Я не могу думать ни о чем другой контроль, который сложен достаточно, чтобы иметь возможность добавлять элементы в Самостоятельная коллекция.
Противоположный Сценарий, когда у вас есть ListView привязан к вашей коллекции и команде что указывает на намерение пользователя добавить новый элемент - затем в обработчик команд вы просто добавляете новый элемент в DataModel и пусть односторонняя синхронизация сделает остальное работа. В этом случае ListView не является возможность добавить в коллекцию подарки.

Что касается самого процесса синхронизации, посмотрите на проект Bindable LINQ - он может минимизировать количество кода и улучшить читаемость. Например, код, который разместил Том, будет выглядеть примерно так:

class ViewModel
{
  public Model Model;
  public ObservableCollection<FooView> Foos;
  public ViewModel()
  {
    Foos = from foo in Model.Foos.AsBindable()
           select new FooView(foo);
  }
}

РЕДАКТИРОВАТЬ 2: После использования B-LINQ в течение некоторого времени я должен сказать, что у вас могут быть проблемы с производительностью с ним. Я использовал его для синхронизации относительно больших коллекций (сотен элементов) с десятками элементов, которые добавляются и удаляются каждую секунду, и мне пришлось отказаться от этого и реализовать синхронизацию, как предложил Том.
Я все еще использую B-LINQ, хотя в тех частях проекта, где коллекции невелики и производительность не является проблемой.

1 голос
/ 01 марта 2010

Я предложил общую структуру Undo / Redo, основанную на MVVM, которая использует некоторые методы, относящиеся к описываемой вами проблеме. Он использует коллекции, реализующие этот интерфейс:

public interface MirrorCollectionConversor<V, D>
{
   V GetViewItem(D modelItem, int index);
   D GetModelItem(V viewItem, int index);
}

(V для элементов ViewModel, D для элементов модели)

Используя этот интерфейс, он автоматически синхронизирует коллекцию viewmodel при изменении коллекции модели. Если вы изменяете модель представления, это изменение просто перенаправляется в коллекцию моделей.

Функция GetViewItem дает вам некоторую гибкость в том, как объекты модели представления связаны с его аналогами модели.

Подробности вы можете найти здесь .

(Я признаю, что конструкция довольно сложная, и я буду очень рад выслушать предложения).

1 голос
/ 16 февраля 2010

Я написал несколько вспомогательных классов для упаковки наблюдаемых коллекций бизнес-объектов в их аналоги View Model здесь , возможно, его следует расширить, чтобы пойти другим путем. всегда ищу вклад ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...