Инициализация модели представления - PullRequest
10 голосов
/ 01 сентября 2010

Что-то, что продолжает сбивать меня с толку в MVVM - если я использую подход "просмотр в первую очередь" для конструирования своих объектов (это, кажется, самый распространенный подход, по крайней мере, после большого чтения и поиска), как мне получить контекстную информацию вмодель представления?

Я видел много ответов на подобные вопросы: «Используйте контейнер DI для внедрения вашей модели», но это мне здесь не поможет, поэтому я приведу небольшой пример.

Допустим, мое приложение - PeopleEditor.Это сделано для загрузки и редактирования объектов People, которые являются сложными.Когда вы загружаете приложение, вы получаете домашний экран, который загружает кучу людей в память - скажем, все они доступны через коллекцию, к которой я могу обратиться из своего контейнера.Нажав на персонажа, вы попадете на экран редактора.Редактор сложный, так что это не тривиальное представление основной детали, реализованное на одном экране.

Итак, на главном экране, когда я щелкаю человека, приложение должно создать новый вид и модель представления ипоказать вид.Если я сначала создаю модель представления через контейнер или нет, я могу инициализировать его с соответствующим объектом person. Это кажется мне очень естественным, поэтому мне трудно понять, почему представление "первый взгляд" является преобладающим шаблоном. Как бы я это сделал, используя подход "вид сначала"?Представление создаст модель представления, которая может попасть в коллекцию людей, но не знает, какой человек ее редактирует.РЕДАКТИРУЙТЕ для ясности: одновременно могут существовать несколько редакторов People, каждый из которых редактирует отдельного человека.

Ссылочная реализация MVVM в альфа-версии Prism 4.0 использует «обработчик состояний», который в основном является службой, которую приложение использует для храненияпараметр конструктора в контейнере.Он сохраняет состояние и вызывает ShowView, а созданная в конечном итоге модель представления импортирует объект состояния.Это кажется мне неуклюжим - как будто он пытается притвориться, что он слабо связан, когда на самом деле это не так.У кого-нибудь есть еще какие-нибудь советы?

Ответы [ 2 ]

3 голосов
/ 01 сентября 2010

nlawalker,

Я не эксперт, но что я узнаю о View-First и Model-First, так это:

  1. View-First: Просмотр программ ViewModel, Вы создаете представление, а затем автоматически создается viewmodel.
  2. Model-First: программы ViewModel View. Вы создаете граф объектов ViewModel в корневом приложении, назначаете его контексту данных корневого представления. затем позволяет представлению визуализировать связанный с ним дочерний объект в зависимости от модели представления.

Не хочу сказать, что подход Model-First плохой, но я предпочитаю подход View-First, потому что viewmodel может находиться в коде позади, поэтому, когда некоторый процесс требует дружественной задачи, не связанной с привязкой (PasswordBox, DialogConfirmation, ClosingForm и т.д.) я могу написать свою логику в коде позади.

В любом случае, для решения кейса я обычно использовал комбинацию IOC и Event Aggregator. Вот оно:

  1. Для viewmodel требует контекстной информации зарегистрировать свой экземпляр в контейнере IOC, чем его тип. Так что Аляс готов даже его мнение не.
  2. Когда происходит навигационное действие (при нажатии на элемент списка людей), разрешите ваш просмотр с помощью распознавателя контейнера IOC. и отправить событие на навигационную шину с указанным параметром. Далее это событие будет отлавливаться целевой ViewModel и что-то делать.

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

UPDATE

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

В вашем случае контекстный объект не является локальным, это скорее сообщение, передаваемое между вызовами объекта. Очевидно, что в подходе, основанном на модели, вы делаете:

//selectedPeople is contextual object
myPeopleDetailVM.LoadData(selectedPeople)

будет примерно так же, когда вы передадите selectedPeople аргументу шины событий.

Если вы рассматриваете производительность, то вы можете сравнить ее с WPF Routed Event System , в этом случае стратегия маршрутизации является более сложной, чем Event Bus, и я думаю, если вы достаточно уверены в том, что используете WPF Routed Event, вы должны с агрегатором событий.

Единственная проблема, которую я вижу, если вы используете встроенный каркасный агрегатор событий (prism, mvvmlight), ваша viewmodel загрязнена шиной событий, если вы жалуетесь на это, то я согласен с вами.

Надеюсь, что поможет.

0 голосов
/ 21 января 2014

Если вы используете Prism, вы можете легко и аккуратно решить эту проблему, используя функцию навигации.Используйте IRegionManager.RequestNavigate, чтобы перейти от основного представления к представлению редактирования, создав Uri целевого представления, чтобы включить параметр строки запроса для идентификации соответствующего лица.Вы можете извлечь этот идентификатор в реализации метода OnNavigatedTo () модели целевого представления (член INavigationAware. Модель представления должна реализовывать этот интерфейс).

Это можно увидеть в действии в примере приложения «Навигация по видам»который поставляется с загрузкой Prism.Он находится в папке Quickstarts.

Из того же примера приложения (которое имитирует Outlook) этот следующий код используется для перехода от InboxView к EmailView, чтобы открыть определенное письмо из папки «Входящие»:

var builder = new StringBuilder();
builder.Append(EmailViewKey);
var query = new UriQuery();
query.Add(EmailIdKey, document.Id.ToString("N"));
builder.Append(query);
this.regionManager.RequestNavigate(RegionNames.MainContentRegion, new Uri(builder.ToString(), UriKind.Relative));

И в модели представления EmailView EmailViewModel открываемое письмо извлекается из контекста навигации следующим образом:

 void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
    {
        // todo: 15 - Orient to the right context
        //
        // When this view model is navigated to, it gathers the
        // requested EmailId from the navigation context's parameters.
        //
        // It also captures the navigation Journal so it
        // can offer a 'go back' command.
        var emailId = GetRequestedEmailId(navigationContext);
        if (emailId.HasValue)
        {
            this.Email = this.emailService.GetEmailDocument(emailId.Value);
        }

        this.navigationJournal = navigationContext.NavigationService.Journal;
    }

 private Guid? GetRequestedEmailId(NavigationContext navigationContext)
    {
        var email = navigationContext.Parameters[EmailIdKey];
        Guid emailId;
        if (email != null && Guid.TryParse(email, out emailId))
        {
            return emailId;
        }

        return null;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...