Для своего WPF-приложения я выбрал MVVM. Вот моя концепция, как я буду реализовывать этот паттерн.
- Мои модели (бизнес-объекты) несут ответственность за проверку (это обязательно для меня).
- ViewModels отвечает за обертку моей модели для удобства взаимодействия с пользователем и некоторых аспектов безопасности.
Мой первый вопрос был о переносе или не переносе моей модели во ViewModel.
- Когда я не оборачиваю свою модель в ViewModel и не открываю модель непосредственно для представления - тогда я не понимаю, зачем мне ViewModel (это кажется бессмысленным)
ViewModel
должен оборачивать Модель по разным причинам:Мне не нравится прямая привязка к строго типизированным свойствам в модели (DateTime, int,…), потому что, когда я это делаю, => WPF берет на себя мою проверку для этих типов. Это действительно плохо, потому что, когда пользователь пишет «aaaa» в Datepicker, моя модель действительна (моя модель никогда не знает об этом, потому что WPF берет на себя контроль над строго типизированными свойствами), а кнопка «Сохранить» включена - это действительно неправильно.
Я не показываю все свойства моей модели представлению, мой
ViewModel
должен защищать мою модель (у меня есть некоторые свойства, которые должны иметь на уровне представления только геттер, а не сеттер)
Мое решение таково, что ViewModel
обязательно следует обернуть Модель. Итак, ViewModel
реализует INotifyPropertyChanged
.
Но теперь у меня проблема с проверкой бизнеса.
Когда я беру красивый IDataErrorInfo, тогда у меня есть все бизнес-правила в ViewModel, что нарушает мою концепцию. Бизнес-правила обязательно должны быть в модели.
Пример: когда пользователь выбирает тип A, тогда поля 1 и 2 являются обязательными. Когда пользователь выбирает тип B, тогда поле 3 является обязательным - это поле должно быть помечено красным, а кнопка «Сохранить» неактивна, если оно недействительно. Также более тяжелые вещи, такие как свободные / занятые DateTime-Ranges.
Это определенно плохо, когда я делаю это во ViewModel, потому что большинство вещей - это бизнес-часть.
Итак, как я могу этого добиться?
На данный момент у меня есть обходной путь:
Все ValidationRules
находятся в модели как простые методы, например
public string ValidateBirthday(string birthay)
{
if (...)
{
return "Birthday should be…";
}
return string.Empty;
}
В моей ViewModel я реализовал IDataErrorInfo и перенаправлял на мою проверку модели следующим образом:
public string this[string columnName]
{
get
{
switch (columnName)
{
case "Birthday":
return Model.ValidateBirthday(Birthday);
case "XXX":
return Model.ValidateXXX(XXX);
case "YYY":
return Model.ValidateYYY(YYY);
break;
}
}
}
Я никогда не видел ничего подобного (перенаправление на модель) в примере, поэтому очень сомневаюсь в своей реализации.
Мой способ обхода в порядке, или вы видите какие-либо проблемы по этому поводу?
Я пытаюсь дать больше информации о том, что я имею в виду ...
Я знаю о реализации INotifyPropertyChanged и IDataErrorInfo в Модели.
Это хорошо работает с прямым связыванием от представления к модели.
Прямая привязка от представления к модели:
открытый класс PersonViewModel: INotifyPropertyChanged {частное лицо _personModel; общедоступный человек PersonModel {получить {вернуть _personModel; } установить {если (_personModel! = значение) {_personModel = значение; NotifyPropertyChanged (); }}}
public PersonViewModel(Person person) { PersonModel = person; } …
}
Вид:
<DatePicker Text="{Binding PersonModel.Birthday}"/>
Большой недостаток: WPF берет на себя управление всеми свойствами со строгим типом.
Пример: пользователь ввел 20.07.2008 в datepicker, поэтому PersonModel будет проинформирован, и PersonModel может это проверить, когда OK, тогда PersonModel действителен => SaveButton включен.
Теперь пользователь ввел «aaa» в datepicker, WPF берет на себя управление этой проверкой, потому что это привязка к строго типизированному свойству (DateTime). PersonModel не будет проинформирован об этом, поэтому PersonModel все еще действителен => SaveButton включен!
Так что для этой «проблемы» мне нужен ViewModel
правильно.
ViewModel оберните модель следующим образом:
открытый класс PersonViewModel: INotifyPropertyChanged {частное лицо _personModel;
public string Birthday { get { if (_personModel. Birthday!= null) { return ((DateTime) _personModel. Birthday).ToShortDateString(); } else { return String.Empty; } } set { if (_personModel. Birthday.ToString() != value) { DateTime dateValue; if (DateTime.TryParse(value, out dateValue)) { _personModel.Birthday = dateValue; … } else { … } } } } public PersonViewModel(Person person) { _personModel = person; } …
}
Теперь я не привязываю Модель напрямую из View. Я привязываю Свойства из ViewModel, который обернул Модель.
<DatePicker Text="{Binding Birthday}"/>
Большое преимущество: теперь у меня есть полный контроль над тем, что пользователь вводит в поля. Когда пользователь вводит строки типа «aaa» в Datepicker, я могу поймать это => установить состояние недействительным, а SaveButton отключен.
Это одна из причин, по которой я не использую прямую привязку из представления к модели. Другая причина - это свойство только для чтения. В модели я получил и установил каждое свойство, но из соображений безопасности я не буду предлагать все свойства из модели с получением и установкой. Таким образом, это также можно решить с помощью ViewModel, заключив в свойства только get. С прямым связыванием вы не можете делать всего этого.
Я хочу сказать, что я обязательно перенесу все свойства из моей модели в ViewModel, но как я могу использовать красивый IDataErrorInfo
в модели (он работает только с прямым связыванием)?
IDataErrorInfo
в классах модели или типа данных, а не в модели представления. При правильном написании WPF, MVVM иIDataErrorInfo
интерфейс на самом деле прекрасно сочетаются друг с другом. Кроме того, модель представления не просто оборачивает класс модели ... вместо этого она должна предоставлять все необходимые данные и функции для связанного с ним представления. Пожалуйста, пойдите и прочитайте об этих технологиях / методологиях, прежде чем приходить сюда, чтобы жаловаться на них, не понимая. Это не веб-сайт, на который вы можете прийти, чтобы узнать об этом. - person Sheridan   schedule 06.05.2014string
s, а? Вы потеряете все встроенные функции типа .NET, такие какstring.Format
и т. Д., Так что удачи вам с этой идеей! Это так не лучший вариант. ИнтерфейсIDataErrorInfo
предназначен для ошибок данных, а не ошибок пользовательского интерфейса ... ключ кроется в названии. Если пользователь вводит «AAA» вDatePicker
, зачем вам знать это недопустимое значение в модели? «большой недостаток», о котором вы говорите, на самом деле вовсе не недостаток ... по крайней мере, ни я, ни многие мои пользователи моих приложений WPF никогда не считали это проблемой. - person Sheridan   schedule 06.05.2014IDataErrorInfo
, и мы проверяем ошибки пользовательского интерфейса (например, пользователь пытается ввести букву в числовое поле) в пользовательском интерфейсе, обрабатывая события. - person Sheridan   schedule 06.05.2014DateTime
по умолчанию какDateTime.MinValue
илиDateTime.MaxValue
, а затем использовал быConverter
для обнаружения любого из этих значений и вывода сообщенияNot set
в фактическом элементе управления. - person Sheridan   schedule 06.05.2014