WPF. Должен ли пользовательский элемент управления иметь свой собственный ViewModel?

У меня есть окно, состоящее из нескольких пользовательских элементов управления, и задалось вопросом, имеет ли каждый пользовательский элемент управления собственный ViewModel или если у windows в целом есть только один ViewModel?

Это не вопрос «да» или «нет». Это зависит от того, дает ли вам дополнительные модели обзора лучшую удобство и возможность проверки. Нет смысла добавлять модели просмотра, если он ничего не принесет вам. Вам нужно будет определить, стоит ли накладные расходы в вашем конкретном случае использования.

Абсолютно, положительно

НЕТ

У ваших UserControls не должно быть ViewModels, специально разработанных для них. Это, по сути, запах кода. Он не нарушает ваше приложение сразу, но это вызовет у вас боль при работе с ним.

UserControl – простой способ создать элемент управления с использованием. UserControls по-прежнему являются элементами управления и поэтому должны быть исключительно связаны с вопросами пользовательского интерфейса.

Когда вы создаете ViewModel для своего UserControl, вы либо размещаете бизнес-логику, либо логику пользовательского интерфейса. Неправильно использовать ViewModels для хранения логики пользовательского интерфейса, поэтому, если это ваша цель, отбросьте свою виртуальную машину и поместите код в код. Если вы размещаете бизнес-логику в UserControl, скорее всего, вы используете ее для разделения частей своего приложения, а не для упрощения создания контроля. Элементы управления должны быть простыми и иметь одну цель, для которой они предназначены.

Когда вы создаете ViewModel для вашего UserControl, вы также нарушаете естественный stream данных через DataContext. Здесь вы испытаете большую боль. Чтобы продемонстрировать это, рассмотрим этот простой пример.

У нас есть ViewModel, который содержит людей, каждый из которых является экземпляром типа Person.

public class ViewModel { public IEnumerable People { get; private set; } public ViewModel() { People = PeopleService.StaticDependenciesSuckToo.GetPeople(); } } public class Person { public string Name { get; set; } public int Age { get; set; } } 

Чтобы показать список людей в нашем окне, тривиально.

            

Список автоматически выбирает правильный шаблон элемента для Лица и использует PersonView для отображения информации пользователя пользователю.

Что такое PersonView? Это UserControl, который предназначен для отображения информации человека. Это элемент управления дисплеем для человека, подобно тому, как TextBlock является элементом управления отображением текста. Он предназначен для привязки к Лику и, как таковой, работает плавно. Обратите внимание, что в окне выше показано, как ListView передает каждый экземпляр Person в PersonView, где он становится DataContext для этого поддерева визуального.

         

Чтобы это работало гладко, ViewModel UserControl должен быть экземпляром Type, для которого он предназначен . Когда вы нарушаете это, делая глупые вещи, как

 public PersonView() { InitializeComponent(); this.DataContext = this; // omfg } 

или

 public PersonView() { InitializeComponent(); this.DataContext = new PersonViewViewModel(); } 

вы нарушили простоту модели. Обычно в этих случаях вы оказываетесь в отвратительных обходных решениях, наиболее распространенным из которых является создание свойства pseudo-DataContext для того, каким должен быть ваш DataContext. И теперь вы не можете привязать одного к другому, так что в итоге вы получаете ужасные хаки

 public partial class PersonView : UserControl { public PersonView() { InitializeComponent(); var vm = PersonViewViewModel(); // JUST KILL ME NOW, GET IT OVER WITH vm.PropertyChanged = (o, e) => { if(e.Name == "Age" && MyRealDataContext != null) MyRealDataContext.Age = vm.PersonAge; }; this.DataContext = vm; } public static readonly DependencyProperty MyRealDataContextProperty = DependencyProperty.Register( "MyRealDataContext", typeof(Person), typeof(PersonView), new UIPropertyMetadata()); public Person MyRealDataContext { get { return (Person)GetValue(MyRealDataContextProperty); } set { SetValue(MyRealDataContextProperty, value); } } } 

Вы должны думать о UserControl как о более сложном контроле. Имеет ли TextBox собственный ViewModel? Нет. Вы привязываете свойство своей виртуальной машины к свойству Text элемента управления, и элемент управления показывает ваш текст в пользовательском интерфейсе.

MVVM не поддерживает «No Codebehind». Поместите логику пользовательского интерфейса для вашего пользовательского элемента управления в код. Если он настолько сложный, что вам нужна бизнес-логика внутри пользовательского элемента управления, это предполагает, что он слишком всеобъемлющий. Упрощать!

Подумайте о UserControls в MVVM, как это: для каждой модели у вас есть UserControl, и она предназначена для представления данных в этой модели пользователю. Вы можете использовать его в любом месте, где хотите показать пользователю эту модель. Нужна ли вам кнопка? Выделите свойство ICommand на свой UserControl и позвольте вашей бизнес-логике связываться с ним. Нужно ли вашей бизнес-логике знать, что происходит внутри? Добавить маршрутизируемое событие.

Обычно, в WPF, если вы спрашиваете, почему больно что-то делать, это потому, что вы не должны этого делать.

Я бы сказал, что каждый пользовательский элемент управления должен иметь свой собственный ViewModel, потому что это позволит вам повторно использовать пару ViewModel / UserControl в новых созвездиях в будущем.

Насколько я понимаю, ваше окно представляет собой составной элемент пользовательских элементов управления, поэтому вы всегда можете создать ViewModel, который объединяет все отдельные ViewModels для каждого из элементов управления пользователя. Это даст вам лучшее из обоих миров.

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

В качестве дополнительного бонуса ваше приложение будет более подходящим для перехода на более архитектурно-звуковую композиционную модель, как это предусмотрено рамками Prism или Caliburn, если возникнут требования приложения.

  • Как правильно привязать свойство зависимостей usercontrol в рамках MVVM
  • Динамическое обновление содержимого TabControl во время выполнения
  • Можно ли связать свойство Canvas's Children в XAML?
  • WPF - привязка к индексу предметов изнутри ItemTemplate of ItemsControl?
  • Как вы можете клонировать объект WPF?
  • Прослушать изменения свойства зависимостей
  • Плавная текстовая анимация (Marquee) с использованием WPF
  • Пользовательский курсор в WPF?
  • Почему RelayCommand
  • WPF: привязать DataGrid к списку
  • Как я могу программно генерировать события нажатия клавиш на C #?
  • Давайте будем гением компьютера.