5

Работали мы когда-то с WinForm и был у нас некий класс:

class City
{
    public int ID {get;set;}
public string Name {get;set;}

public List<Area> {get;set;}

.... }

Потом мы узнали про WPF и решили перейти на него. Но если это WPF, то занчит надо делать все по канону (т.е. с использованием MVVM) и надо бы сделать ViewModel. Дак вот тут-то я и теряюсь. Как лучше сделать? Доработать тот самый класс City - сделать его наследником INotifyPropertyChanged (При этом коллекции заменить на ObservableCollection), но ведь тогда у нас Model и ViewModel сольются воедино? Не хорошо получается, или хорошо?. Или же создать для него ViewModel:

class CityViewModel : INotyPropertyCjanged
{
    private City _city;
public int ID 
{
   get {return _city.ID; }
   set {_city.ID = value; RaisePropertyChanged("ID");}
}
...

}

Но что же тогда делать с List<Area>? Как ее упаковать в ObservableCollection?

Направьте пожалуйста на путь истинный... Примеры приветствуются!

UPD

И пока не забыл. Допустим нам надо наполнить нашу модель данными из, например, баз данных. Где должна лежать функция получения данных? Во ViewModel или View или еще где?

Donil
  • 4,212
  • функция получения данных должна бытьв отдельном классе DataContext который передаётся в Model – Виталик Dec 07 '13 at 11:30

1 Answers1

4

Смотрите.

Классы модели лежат отдельно, и не пересекаются с классами VM. Классы VM моделируют сущности вашей «бизнес-логики», то есть, внутренние объекты в терминах программы. Классы модели моделируют сущности своей предметной области.

Например, ID интересен модели, но может быть вовсе не интересен программе, так что переносить его в VM нет особого смысла (разве что он нужен в UI).

Таким образом, у нам получается вот что:

Модель:

class City
{
    public int ID { get; private set; }
    public string Name { get; private set; }
    public List<Area> Areas { get; private set; }
}

static class CityHelpers { static City GetCityByName(string name) { ... } }

VM:

class CityVM : INotifyPropertyChanged // или даже DependencyObject
{
    public CityVM(City city)
    {
        name = city.Name;
        Areas = new ObservableCollection();
        foreach (var area in city.Areas)
            Areas.Add(new AreaVM(area));
    }
public Name
{
    get { return name; }
    set
    {
        if (name == value) return;
        name = value;
        RaisePropertyChanged("Name");
    }
}

public ObservableCollection&lt;AreaVM&gt; Areas { get; private set; }

// ну и ещё имплементация INotifyPropertyChanged

}


Функции получения данных лежат, понятно, в модели: VM не должна знать, как именно и откуда берутся данные. Но вот попросить модель загрузить данные должна именно VM (причём желательно грузить их не в UI-потоке, конечно, а то будет подвисать).

VladD
  • 206,799
  • Не думаю что это правильное решение использовать List в модели так как после обновлении списка Areas в модели City список Areas в CityVM тоже придётся обновлять, рекомендую сразу использовать ObservableCollection в модели а в VM просто сделать свойсто которое возвращает список из модели – Виталик Dec 07 '13 at 12:28
  • Есть пара вопросов:
    1. Как модель должна узнать о координатах хранилища? Передавать ей класс, который "знает" или же пердавать сами координаты (ConnectionString в случае с БД)?
    2. Что если мы работаем с уже готовыми классами моделей (у нас нет возможности их довести до нужной функциональности)? Обернуть их в еще один класс, который мы и будем дальше использовать как модель?
    – Donil Dec 07 '13 at 12:29
  • 1
    @Виталик: Если данные модели «живые», то есть могут изменяться без прямой на то санкции VM, модель должна иметь средство оповещения об изменениях. Например, event или INotifyCollectionChanged. ObservableCollection использовать в модели тоже можно, но это не всегда лучшее решение, так как ObservableCollection привязана к потоку. – VladD Dec 07 '13 at 12:39
  • @Donil: координаты хранилища — внутренняя подробность устройства модели, «внешний мир» о них не знает ничего. ConnectionString пусть хранится у модели, в одном из модельных классов. Или пусть хранится в конфигурации, а один из модельных классов будет её оттуда доставать (и вообще будет за неё ответственным). – VladD Dec 07 '13 at 12:42
  • @VladD,а что если эти координаты задает пользователь при входе в программу, например? В моем предствлении модель это просто модель - набор полей, свойств и вспомогательных методов для получения дополнительной информации, выводимой из полей и свойств. А данными из какого либо хранилища должен наполнять какой-то хэлпер. При таком подходе мы избавляемся от привязке к конкретному хранилищу и пользователь сам может вырать нужное ему из доступных – Donil Dec 07 '13 at 12:53
  • Если координаты задаёт пользователь (это достаточно нетипично, но всё же), значит, поставка координат модели идёт через VM, конечно (которая имеет отдельный View для того, чтобы получить координаты от пользователя).

    Модель должна инкапсулировать всё, что может (уменьшение зависимостей есть гут), но не больше.

    – VladD Dec 07 '13 at 13:01
  • @VladD, Ваши доводы более чем убедительны.

    это достаточно нетипично, но всё же

    Я как раз таки имею с подобным дело, поэтому и появился такой вопрос.

    Тогда, я думаю, класс CityHelpers нужно сделать публичным? Чтобы ViewModel могла инициировать получение данных.

    – Donil Dec 07 '13 at 13:08
  • 2
    @Donil: Угу, он планировался как публичный. Вы как архитектор определяете интерфейс между моделью и VM. Лучше даже распланировать его на бумаге заранее, прикинуть типичный workflow, кто кого когда вызывает, кто чем заведует, кто о чём знает. – VladD Dec 07 '13 at 13:11
  • Примного благодарен за столь подробный ответ! Теперь все окончательно встало на свои места:) – Donil Dec 07 '13 at 13:17
  • @Donil: Пожалуйста! Обращайтесь, на хорошие вопросы приятно отвечать. – VladD Dec 07 '13 at 13:18