MVVM - что является идеальным способом для пользовательского контроля разговаривать друг с другом
У меня есть пользовательский элемент управления, который содержит несколько других пользовательских элементов управления. Я использую MVVM. Каждый пользовательский элемент управления имеет соответствующую виртуальную машину. Как эти пользовательские элементы управления передают информацию друг другу? Я хочу избежать написания кода в коде xaml. В частности, меня интересует, как элементы управления (внутри основного пользовательского элемента управления) будут разговаривать друг с другом и как они будут разговаривать с пользовательским элементом управления контейнером.
ИЗМЕНИТЬ:
Я знаю, что использование событий-делегатов поможет мне решить эту проблему. Но я хочу избежать написания кода в коде xaml.
Ответы
Ответ 1
Как правило, лучше всего попытаться уменьшить объем связи между частями, так как каждый раз, когда два пользовательских элемента управления "разговаривают" друг с другом, вы вводите зависимость между ними.
При этом есть несколько вещей, которые следует учитывать:
- UserControls всегда может "разговаривать" с их содержащим элементом управления, выставляя свойства и используя DataBinding. Это очень приятно, так как он сохраняет стиль MVVM во всех аспектах.
- Содержащий элемент управления может использовать свойства для "связывания" двух свойств на двух пользовательских элементах управления, опять же, сохранения чистых границ.
Если вам нужно иметь более явное сообщение, есть два основных подхода.
- Реализовать службу, общую для обоих элементов, и использовать Injection Dependency для обеспечения реализации во время выполнения. Это позволяет средствам управления разговаривать с сервисом, что, в свою очередь, позволяет синхронизировать элементы управления, но также сохраняет минимальную зависимость.
- Используйте некоторые формы обмена сообщениями для передачи сообщений между элементами управления. Многие основы MVVM используют этот подход, поскольку он отделяет отправку сообщения от приема сообщения, опять же, сохраняя зависимости до минимума.
Ответ 2
Ваша концептуальная проблема здесь:
Каждый пользовательский элемент управления имеет соответствующую виртуальную машину.
Наличие отдельной ViewModel для каждого вида в значительной степени поражает концепцию ViewModel. ViewModels не должны быть взаимно однозначными с представлениями, в противном случае это ничего, кроме прославленного кода.
ViewModel отображает концепцию "текущего состояния пользовательского интерфейса" - например, какая страница вы находитесь и независимо от того, редактируете ли вы - в отличие от "текущих значений данных".
Чтобы действительно воспользоваться преимуществами M-V-VM, определите количество классов ViewModel, используемых на основе отдельных элементов, которые нуждаются в состоянии. Например, если у вас есть список элементов, каждый из которых может отображаться в трех состояниях, вам нужна одна виртуальная машина для каждого элемента. Напротив, если у вас есть три представления, все из которых отображают данные тремя различными способами в зависимости от общей настройки, общая настройка должна быть записана в одной виртуальной машине.
Как только вы создадите ViewModels, чтобы отразить требования к задаче, вы обычно обнаруживаете, что нет нужды и желания связывать состояние между представлениями. Если есть такая необходимость, лучше всего пересмотреть вашу модель ViewModel, чтобы узнать, может ли общий ViewModel получить доступ к небольшому количеству дополнительной информации о состоянии.
Бывают случаи, когда сложность приложения диктует использование нескольких ViewModels для одного и того же объекта модели. В этом случае ViewModels могут содержать ссылки на общий объект состояния.
Ответ 3
Для этого существует много разных механизмов, но сначала вы должны узнать, в каком слое вашей архитектуры принадлежит это сообщение.
Одна из целей структуры MVVM заключается в том, что различные представления могут быть сделаны в одной и той же модели представления. Будут ли эти пользовательские контроллеры разговаривать друг с другом только в том представлении, которое вы в настоящее время реализуете, или им придется разговаривать друг с другом в других возможных представлениях? В последнем случае вы хотите реализовать его ниже уровня представления, либо в модели viewmodel, либо в самой модели.
Примером первого случая может быть, если ваше приложение работает на очень маленькой поверхности дисплея. Возможно, ваш пользовательский контроль должен конкурировать за визуальное пространство. Если пользователь нажимает один пользовательский контроль для максимизации, остальные должны минимизировать. Это не имеет ничего общего с моделью просмотра, это просто адаптация к технологии.
Или, может быть, у вас разные режимы просмотра с различными пользовательскими настройками, где все может произойти без изменения модели. Примером этого может быть навигация. У вас есть список чего-то, а также панель сведений с полями и командами, которые подключены к выбранному элементу в списке. Вы можете захотеть unit test логику включения кнопок для каких элементов. Модель не связана с тем, на какой элемент вы смотрите, только при нажатии кнопок кнопок или изменении полей.
Потребность в этом сообщении может даже быть в самой модели. Возможно, у вас есть денормализованные данные, которые обновляются, потому что другие данные изменены. Затем различные режимы просмотра, которые находятся в действии, должны измениться из-за рябь изменений в модели.
Итак, подведем итог: "Это зависит..."
Ответ 4
Я думаю, что лучшим решением будет использование шаблона Publisher/Subscriber. Каждый элемент управления регистрирует некоторые события и прикрепляет ошибки к событиям, обнаруженным другими элементами управления.
Чтобы выявить события и приложить к ним, вам нужно будет использовать какой-либо посредник /EventBroker. Я нашел хороший пример здесь
Ответ 5
Лучший способ сделать это, на мой взгляд, - Commanding (Routed Commands/RelayCommand и т.д.).
Я хочу избежать написания кода в коде xaml.
Хотя это похвальная цель, вам нужно применить немного практичности к этому, она не должна применяться на 100% как правило типа "ты не будешь".
Ответ 6
Вы можете связываться между элементами в пользовательском интерфейсе, используя привязку элементов, поэтому, предполагая, что пользовательский элемент управления, созданный вами, предоставляет свойство, другие пользовательские элементы управления могут привязываться к нему. Вы можете настроить привязку, использовать свойства зависимостей вместо базовых свойств/реализовать INotifyPropertyChanged, но это теоретически возможно, но для этого необходимо предусмотреть некоторую предусмотрительность.
Скорее всего, вам будет гораздо проще использовать комбинацию событий, кода и свойств, чем попробовать чистый декларативный способ, но теоретически возможно.
Ответ 7
Вы можете поделиться некоторыми объектами View Model между элементами управления, а также с командами...
Например, у вас есть основной элемент управления, который содержит два других элемента управления. И у вас есть некоторые функции фильтрации в основном элементе управления, но вы хотите разрешить пользователю устанавливать часть фильтра в первом суб-контроле (например, "Полный фильтр" ) и некоторой части фильтра в другом (например, "Быстрый фильтр" "). Также вы хотите начать фильтрацию с любого из суб-элементов управления. Затем вы можете использовать такой код:
public class MainControlViewModel : ObservableObject
{
public FirstControlViewModel firstControlViewModel;
public SecondControlViewModel firstControlViewModel;
public ICommand FilterCommand;
public FilterSettings FilterSettings;
public MainControlViewModel()
{
//...
this.firstControlViewModel = new FirstControlViewModel(this.FilterSettings, this.FilterCommand);
this.secondControlViewModel = new SecondControlViewModel(this.FilterSettings, this.FilterCommand);
}
}
public class FirstControlViewModel : ObservableObject
{
//...
}
public class SecondControlViewModel : ObservableObject
{
//...
}
В главном элементе управления XAML вы привяжете субконтроллеры DataContext к соответствующим View Models. Всякий раз, когда под-контроль изменяет настройку фильтра или выполняет команду, будет уведомлен другой подконтроль.
Ответ 8
Как говорили другие, у вас есть несколько вариантов.
Предоставление DepedencyProperties в пользовательских элементах управления и привязка к этим свойствам обеспечивает в большинстве случаев чистое решение XAML, но может вводить некоторые зависимости пользовательского интерфейса, чтобы привязки могли видеть друг друга
Другой вариант - это развязанный шаблон обмена сообщениями для отправки сообщений между ViewModels. Я хотел бы, чтобы пользовательские элементы управления связывались с свойствами собственной виртуальной машины, а затем при изменении свойств внутри этой виртуальной машины он мог "публиковать" сообщение, которое уведомляет других "подписчиков" о том, что что-то произошло, и они могут реагировать на это сообщение, однако они хотят.
У меня есть сообщение в блоге по этой теме, если это помогает: http://www.bradcunningham.net/2009/11/decoupled-viewmodel-messaging-part-1.html
Ответ 9
Если вы используете строгий MVVM, тогда пользовательский элемент управления представляет собой представление и должен только "говорить" или, скорее, связывать его с ViewModel. Так как ваши ViewModels, скорее всего, уже реализуют INotifyPropertyChanged, если у них есть ссылка на друг друга, они могут использовать события PropertyChanged для уведомления при изменении свойств или они могут вызывать методы (лучше, если через интерфейс) связываться с каждым другие.