MVVM: слабая связь ViewModels

У меня есть классы, A, B, C, D

A имеет ссылку на E и B и на список C и D.

В AViewModel они представлены в виде дерева.

Вид для А выглядит так, как на картинке.

введите здесь описание изображения

Когда узел выбран, должно отображаться соответствующее представление.

Существуют ViewModels для E, B, C и D.

Мой вопрос заключается в следующем:

Например, E — выбранный узел. Я храню его в AViewModel как «объект SelectedItem». Каков наилучший способ создать EViewModel слабо связанным способом, чтобы я не хотел ссылаться на EViewModel в AViewModel.

Обновление:

Я думал об одном решении, но я никогда не видел его в других местах:

Например, я мог выставить только свои POCO (B, C, D, E) из AViewModel. А в XAML я мог напрямую привязать ContentControl к этим объектам. С преобразователем у меня может быть BViewModel, когда я привязываюсь к B и так далее.


person jannagy02    schedule 20.01.2017    source источник
comment
Не могли бы вы опубликовать свой xaml, чтобы нам не пришлось изобретать велосипед?   -  person lokusking    schedule 20.01.2017


Ответы (2)


Распространенным и широко распространенным способом связи между моделями представлений и другими компонентами в слабосвязанном виде в приложении MVVM было бы использование агрегатора событий или мессенджера. Пожалуйста, обратитесь к следующим ссылкам для получения дополнительной информации.

Использование шаблона агрегатора событий для связи между моделями представлений: https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-связь-между-представлениями-моделями/

MVVM — службы обмена сообщениями и просмотра в MVVM: https://msdn.microsoft.com/en-us/magazine/jj694937.aspx.

Другим вариантом может быть использование общей службы, с помощью которой вы внедряете модели представлений: пример использования общих сервисов-призма? форум=wpf" rel="nofollow noreferrer">https://social.msdn.microsoft.com/Forums/en-US/22907a0f-d805-4195-8272-7c284b72d2ee /example-of-using-shared-services-prism?forum=wpf

Использование агрегатора событий, мессенджера или общего сервиса означает, что вы можете удалить все ссылки между классами модели представления.

Вместо того, чтобы классы модели представления имели строгие ссылки друг на друга туда и обратно, каждая модель представления знает только об одном агрегаторе/мессенджере/общем сервисе событий и взаимодействует только с ним. Модель представления A может отправлять любое количество сообщений, на которые могут подписаться любые другие модели представлений для прослушивания и внутренней обработки.

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

Создание экземпляра другой модели представления без создания связи строк невозможно. Если одна модель представления создает экземпляр другой модели представления, они по определению сильно связаны. Чтобы предотвратить это, вы можете внедрить модель представления с типом интерфейса, который реализует другая модель представления, например:

public ViewModelB(IViewModelA viewModelA)
{
    //...
}

Тогда ViewModelB зависит от типа интерфейса, а не от конкретной реализации ViewModelA. Это немного лучше, чем делать что-то подобное, потому что тогда ViewModelA и ViewModelB всегда будут, как упоминалось выше, сильно связаны друг с другом:

public ViewModelB()
{
    _viewModelA = new ViewModellA();
}

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

person mm8    schedule 20.01.2017
comment
Мой вопрос касается лучших практик по созданию экземпляра ViewModel без тесной связи, а не связи между слабо связанными ViewModels. - person jannagy02; 21.01.2017
comment
Это невозможно. Если одна модель представления создает экземпляр другой модели представления, они по определению сильно связаны. Смотрите мой отредактированный ответ для получения дополнительной информации. - person mm8; 22.01.2017
comment
@ jannagy02 Используйте фабричный метод и верните (экземпляр) интерфейса. Или используйте DI-контейнер, такой как Prism — вы запрашиваете тип (используете интерфейсы) и получаете конкретный тип обратно. Но для того, что вы указали, это звучит так, как будто B, C, D и E - все это POCO (которые могут быть обернуты своими собственными моделями просмотра) - для AViewModel нормально знать о них, потому что его работа заключается в формировании, преобразовании и представлении данных для вид. - person slugster; 22.01.2017
comment
@ mm8 Это не невозможно, например, с помощью фабричного метода, о котором также упоминал slugster. Я добавил обновление в свой пост. - person jannagy02; 23.01.2017
comment
Если фабричный метод возвращает экземпляр модели представления B, и вы вызываете фабричный метод в модели представления A, то модель представления A сильно связана с моделью представления B. Прочтите мой ответ еще раз. - person mm8; 23.01.2017
comment
@mm8 Фабричный метод возвращаемого типа может быть объектом или ViewModelBase, но не конкретным типом. - person jannagy02; 23.01.2017
comment
Затем вы можете также поместить любой метод, который вы хотите иметь возможность вызывать в базовом классе напрямую, что означает, что нет смысла создавать экземпляр другой модели представления. - person mm8; 23.01.2017

Если ваш A View отображает «список» плюс выбранное представление, вполне приемлемо иметь ссылку EViewModel в AViewModel. ViewModels может быть «отражением» представлений. Поэтому, если представление A будет содержать EView, AViewModel может содержать EViewModel. Вы просто вкладываете свои модели представления, поэтому создается то же дерево, что и в слое представления.

Кроме того, я бы не ссылался на E или B,... в AViewModel, а ссылался только на EViewModel, BViewModel,... Таким образом, в списке AView отображаются не классы моделей, а классы ViewModel. Ваш SelectedItem набирается как ViewModel, и вы можете напрямую привязать свою часть представления "Display" к SelectedItem. Затем вы можете использовать правильный DataTemplate на слое представления для отображения соответствующего представления. Надеюсь, поможет

person marayfirth    schedule 20.01.2017
comment
Это то, чем я сейчас занимаюсь :) Но я всегда чувствовал, что это можно сделать лучше. - person jannagy02; 20.01.2017