Comments 40
Окончательный вариант можно взять отсюда:
Ссылка отсутствует
Причина в том, что указание StartupUri не позволяет задать DataContext снаружи окна. А хардкодирование DataContext'а внутри XAML неправильно, так как:
- Согласно паттерну MVVM, View не должно руководить VM, и знать о нём должно как можно меньше. То есть код, обслуживающий UI, не должен создавать себе VM сам.
- Чаще всего VM должна получить ещё какие-то аргументы в конструкторе (например, модельные объекты), а это не получится сделать, если VM создаётся в XAML'е (откуда XAML может знать о модели?).
- Иногда перед открытием главного окна нужна дополнительная логика, требующая показа своего маленького окна (например, предложение обновить программу). Код, выясняющий это и при некоторых условиях показывающий дополнительное окно, проще разместить в OnStartup.
- Главная VM может понадобиться вам для ещё чего-нибудь. Поэтому если её создаёт класс App, он может сохранить ссылку на неё у себя, чтобы другие (например, побочное окно) могли быть привязаны к тому же экземпляру VM. Иначе вам придётся лезть за главной VM в экземпляр главного окна.
Не мое, но практика показала что это наиболее разумный подход.
View знает всё про вьюмодель, все свойства, команды и т.д.
Не все. View известен только публичный интерфейс ViewModel. Она не должна знать, откуда брать и как создавать ViewModel. Этим должна заниматься точка сборки.
Если мы поняли, что контекст данных можно получать из контейнера, то остаётся сделать следующий очевидный шаг — декларировать его прямо в XAML через расширение разметки:
Зачем заново придумывать data template?
Плюс в таком виде это не DI, а Service Locator, отягощенный глобальной переменной.
Видно кучу шаблонного кода, большой оверхед без видимой пользы, и куча ViewModels, которые с тем же успехом мог сделать кодогенератор.
Это не удивительно, так как суть паттерна MVVM не просто в отделении представления от модели, а в отделении поведения представления от поведения модели.
Для модели важны консистентность, производительность, отказоустойчивость.
Для представления важно удобство проведения операций в модели для пользователя -человека с наглядной обратной связью, контролем возможных ошибок, возможностью изменить параметры до принятия необратимого решения.
ViewModel нужна как раз для реализации человеко-удобного поведения отдельно от конкретной реализации ввода-вывода.
В статьях этого нет — и ViewModel превращается в скорее вредную, чем бесполезную прокладку.
public IsEditVisible { get; } => (_model.SomeProperty[«SomeIndex»] * _model.GetSomeValue) < _model2.AdjustionCorrelator;
Вьюшка не может собрать по модели — отображать ей кнопку Edit, или нет. А вот к VM одному свойству она прибиндиться может.
IsEditVisible — свойства с таким именем во ViewModel быть не должно, ViewModel ничего не знает о видимости по построению.
Модель может быть такой, облакообразной, как перьевые облака, грозовая бесформенная куча, к которой не прибиндиться
Из этого совершенно не понятно, почему модель именно такая, и что мешает спроектировать ее иначе.
во ViewModel может быть такой код
Если ViewModel можно заменить простым маппером, то она не нужна.
IsEditVisible — свойства с таким именем во ViewModel быть не должно, ViewModel ничего не знает о видимости по построению.
Слушайте, ну у вас какая-то своя философия MVVM, напишите статью о ней и мы там побеседуем о_0
Нужды вью впрямую модели не касаются — именно для этого и вводится VM. Нужды VM формируют интерфейс модели вкупе с основной бизнес задачей (продажей товаров покупателям).
> Если модель используется в проекте в другом месте
… то к нашим услугам адаптеры, мосты и прочие медиаторы. Трехслойные паттерны появились совсем не потому, что модели задавались кем-то внешним. Это способ проектирования и реализации непосредственно приложений с UI.
> Слушайте, ну у вас какая-то своя философия MVVM
У меня нет никакой отдельной философии. Это очень простая вещь — краеугольный камень всех многослойных UI-паттернов. Непосредственно за ввод-вывод информации для человека отвечает слой View и никто больше. Видимость конкретного элемента управления — вопрос исключительно для View. Он может решаться с помощью привязки к свойству ViewModel, но само это свойство всегда выражается в терминах поведения и состояния приложения, а не View, которое может быть абсолютно любым (например, текстовым на шрифте Брайля).
Если у вас ViewModel знает про Edit и что он Visible — вы переложили на нее часть ответственности View и нарушили паттерн.
Нужды VM формируют интерфейс модели вкупе с основной бизнес задачей (продажей товаров покупателям).
Когда я разрабатываю сначала вьюшку, потом к ней создаю VM, а потом, основываясь на вызовах модели из VM, я создаю саму модель — этот подход View-first. Но есть и другой подход (Model-first) — когда сначала без всякого интерфейса создается модель, потом рисуется интерфейс и только потом вьюшки и модель связываются как клеем VM.
Видимость конкретного элемента управления — вопрос исключительно для View. Он может решаться с помощью привязки к свойству ViewModel, но само это свойство всегда выражается в терминах поведения и состояния приложения, а не View, которое может быть абсолютно любым
Вот вроде говорим об одном… но выводы делаем различные.
Вот допустим у нас в представленной задаче кнопочки с покупками определенных товаров были бы видимы только тогда, когда кредита хватало бы на их покупку.
Модели все равно — видны кнопочки или не видны — даже если вы нажмете на такую кнопку: если денег в кредите недостаточно — модель все равно не продаст вам товар. Теперь, если мы (чисто из визуальных нужд) захотим скрыть кнопочки тех товаров, для покупки которых кредит недостаточен, тогда я прописываю во ViewModel if(Credit < MyPrice) BuyVisible = false — условно говоря. А View у меня биндится к BuyVisible. Или это я поставил в кондицию DelegateCommand такую конструкцию. Но в любом случае эту видимость я задаю вo ViewModel. Модели же, напоминаю, пофиг. Она не продаст, даже если ткнуть в невидимую кнопку.
Не BuyVisible, а CanBuy — ViewModel ничего не знает и не хочет знать о видимости.
Но о доступности того или иного действия сообщить может.
А если речь идёт не о доступности действия, а о доступности колонки в Grid на основе роли пользователя.
Например, нужно скрывать колонку с запрплатой, если у пользователя нет соответствующих прав на её простмотр.
В этом случае допускается иметь во ViewModel свойство SalaryVisible (или SalaryAllowed)?
Как тогда по вашему должно выглядеть идеалогически правильное MVVM-решение данной задачи?
К тому же полезные коментарии :).
куча ViewModels, которые с тем же успехом мог сделать кодогенераторКакой генератор, подскажите, вообще какой best practice для разработки поз WPF сейчас?
В private static void Watch<T, T2>
неправильное условие в фильтре
if (a.OldItems?.Count == 1) collToUpdate.Remove(collToUpdate.First(mv => modelProperty(mv) == a.NewItems[0]));`
В статье просто исправил, а в скачиваемом примере — забыл
а почему не использовать ограничения на типы вроде where T: class, new()
это позволило бы избавиться от активатора и писать более понятный и красивый код в виде
new T();
internal interface IInitializable
{
Inject(params object[] p);
}
и таким образом заставить корректно инициализироваться объект.
Ну, это довольно общий случай, тут мог бы подойти вариант попроще, может.
Просто не уверен, что рефлексия — это достаточно хороший в данном случае вариант для создания, хотя я могу быть и не прав :)
А использование асинхронного мессенжера позволит отложить перестройку вьюх, если это необходимо, до более подходящего момента.
Кроме того, вместо источников данных в виде ObservableCollection лучше использовать связку List и ICollectionView. Тогда при изменении данных вьюха не будет перестраиваться при каждом чихе, а будет лишь тогда, когда будет вызван Refresh() для ICollectionView.
Но в данном примере это не обязательно, так как динамических данных нет, все статично.
Что касается ObservableCollection, так смысл как раз в том, чтобы она не руками обновлялась Refresh()'ем, а автоматически. Вьшка вся не перестроится — это не веб:) — автоматически произойдет частичное обновление вью, как и задумывалось.
В конкретной программе есть коллекция с динамическим размером — это список покупок пользователя.
public class MainViewVM : BindableBase {
public int UserSumm { get; }
public ObservableCollection<MoneyVM> UserWallet { get; }
public ObservableCollection<ProductVM> UserBuyings { get; }
public DelegateCommand GetChange { get; }
public int Credit { get; }
public ReadOnlyObservableCollection<MoneyVM> AutomataBank { get; }
public ReadOnlyObservableCollection<ProductVM> ProductsInAutomata { get; }
}
Вместо:
public ReadOnlyObservableCollection<MoneyVM> AutomataBank { get; }
public ReadOnlyObservableCollection<ProductVM> ProductsInAutomata { get; }
Должно быть:
public ObservableCollection<MoneyVM> AutomataBank { get; }
public ObservableCollection<ProductVM> ProductsInAutomata { get; }
Вдруг кто-то как я сидит и руками набивает этот код по мере чтения статьи)
Часть 2: MVVM: полное понимание (+WPF)