Как представить коллекцию (View) моделей в ViewModel - PullRequest
11 голосов
/ 08 мая 2009

У меня есть вопрос относительно дизайна MVVM для C # / WPF. Я посмотрел несколько демо-приложений, но они не решили мою проблему. Мое приложение состоит из объектов, которые содержат другие объекты. Как во взаимоотношениях родителей и детей.

Мой вопрос сейчас:

  • должен ли дочерний атрибут быть ViewModel
  • и если да, то как мне создать новые родительские объекты, которые содержат существующих дочерних объектов через ViewModels?

У меня есть что-то вроде следующего сценария:

class Child {
    string Name;
}

class ChildVM {
    Child _child;
    string Name{return _child.Name;}
}

class Parent {
    string Name;
    List<Child> children;
}

class ParentVM{
    Parent _parent;

    string Name{return _parent.Name;}
    List<ChildVM> children {get;set;}

    ParentVM(Parent p){_parent = p;}
}

void CreateANewParent(){
    List<ChildVM> children = new List<ChildVM>(){new ChildVM(new Child()),...};
    ParentVM parent = new ParentVM(new Parent());
    foreach(ChildVM child in children)
        parent.children.Add(child);
}

Проблема здесь в том, что ParentVM содержит ChildVM, НО фактический Parent (который находится внутри ParentVM) не имеет дочерних объектов, содержащихся в объектах ChildVM. Я также не считаю хорошей идеей дублировать объекты Child, так как это приводит к избыточности, и в контексте моего приложения также нет необходимости / возможности создавать новые объекты Child.

Я также подумал о следующем дизайне класса:

class ParentVM {
    Parent _parent;

    string Name{return _parent.Name;}
    List<Child> children {get{return _parent.Children;}}
}

Однако это будет означать, что я буду напрямую работать с моделью, если захочу манипулировать дочерним объектом ParentVM.

С другой стороны, я мог бы просто оставить (Model) Parent пустым и использовать ParentVM для создания нового Parent в базе данных. Но это хороший способ решения проблемы?

Ответы [ 2 ]

15 голосов
/ 21 мая 2011

На самом деле, правильный способ сделать это, когда вы сначала создаете ParentVM, вы перебираете дочерние элементы переданного Parent, создавая ChildVM для каждого, а затем добавляете эти объекты ChildVM в свойство ChildVMs ParentVM. (Некоторые просто называют это свойство «Дети», но мне лично нравится, если это ясно, что это коллекция ChildVM, а не коллекция объектов Child. Простое добавление суффикса «VM» делает это очень ясным.

Затем вам также необходимо прослушать уведомление об изменении фактической коллекции Parent's Children и соответствующим образом обновить свою коллекцию ChildVM.

Таким образом, у вас есть модель с Parent-> Children-> Child и ViewModel из ParentVM-> ChildVMs-> ChildVM, которая, я считаю, именно то, что вы хотите.

Теперь я также убежден, что вы должны иметь возможность выставлять Parent непосредственно из ParentVM, а также Child непосредственно из ChildVM, поскольку ваш пользовательский интерфейс может быть привязан к различным свойствам этих элементов, таким как ваше имя. свойство выше. Однако пуристы M-V-VM говорят, что никогда не делают этого, говоря, что пользовательский интерфейс никогда не должен знать о модели, потому что, если модель меняется, вы должны изменить пользовательский интерфейс. Мой аргумент заключается в том, что если модель меняется, вам все равно нужно изменить ViewModel по той же причине. Единственная экономия была бы в том случае, если несколько представлений совместно используют одну и ту же ViewModel, поскольку вам просто нужно изменить ее в одном месте, но на самом деле что-то вроде «Имя» не собирается менять свое «имя» с модели на ViewModel, поэтому в любом случае это не аргумент.

Кроме того, производительность снижается из-за «пуристического» подхода, поскольку вы не можете просто делегировать элемент модели, как вы делаете с Name выше, потому что представление никогда не узнает ни о какой модели. сгенерированные изменения в свойстве Name, если вы также не добавите все дополнительные уведомления об изменениях внутри виртуальной машины, то есть теперь у вас есть одно уведомление об изменении в модели, единственной целью которого является создание второго уведомления об изменениях в виртуальной машине, которое затем уведомляет пользовательский интерфейс. Чистый? Да. Производительность инвазивная? Вы держите пари, особенно когда происходит большое количество изменений, и вы используете интерфейс INotifyPropertyChanged, потому что это означает, что вам нужно делать сравнения строк в обработчике изменений, чтобы обнаружить и делегировать все эти изменения! Однако если вы привязываете напрямую к свойству ParentVM.Parent.Name, вы уже получите уведомление об изменении от модели, чтобы уведомить пользовательский интерфейс, и вы также будете поддерживать свою ВМ в чистоте для вещей, которые относятся только к ВМ или представлению.

Однако я никогда не размещаю в модели что-либо, что доступно только для просмотра. Это для меня то, что ViewModel для. Так, например, если у детей есть определенный цвет, основанный на перечислении или чем-то еще, то для меня это то, что входит в ChildVM, а не в саму модель, и если в модели есть какие-либо свойства, которые определяют этот цвет, например, свойство этого перечисления, в этом случае, да, я бы подключил уведомления об изменениях из модели внутри ChildVM. (По правде говоря, я могу даже просто сделать это через преобразователь цвета в пользовательском интерфейсе напрямую, все еще привязываясь к перечислению модели. Это действительно в каждом конкретном случае.)

НТН,

Mark

1 голос
/ 13 мая 2009

Хорошо, я получил некоторую помощь и совет от форума .NET: Я решу проблему, предоставив метод Get для Model внутри ViewModel. Поэтому, если я создаю нового Parent с существующими ChildVM, я просто возвращаю ссылку Child в ChildVM и назначаю их новому Parent.

...