Подтвердить что ты не робот

Где логика навигации принадлежит, View, ViewModel или где-либо еще?

Я кнопка в представлении, связанная с свойством ICommand ViewModel (на самом деле это RelayCommand из mvvv-light)

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

Каков предпочтительный вариант для выполнения вызова NavigationService.Navigate?

4b9b3361

Ответ 1

Если вы уже используете MVVM Light, один из вариантов - использовать шину сообщений, которую он включает. Таким образом, вы привязываете свою кнопку к RelayCommand в модели представления, как вы уже сказали, что уже делаете. В обработчике вашей RelayCommand вы можете принять решение о том, к какому представлению перейти. Это сохраняет всю эту логику в модели представления.

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

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

public class NavigationMessage : NotificationMessage
{
    public string PageName
    {
        get { return base.Notification; }
    }

    public Dictionary<string, string> QueryStringParams { get; private set; }

    public NavigationMessage(string pageName) : base(pageName) { }

    public NavigationMessage(string pageName, Dictionary<string, string> queryStringParams) : this(pageName)
    {
        QueryStringParams = queryStringParams;
    }
}

Это позволяет просто передавать имя страницы или, необязательно, также включать любые необходимые параметры строки запроса. Обработчик RelayCommand опубликовал бы это сообщение так:

private void RelayCommandHandler()
{
    //Logic for determining next view, then ...
    Messenger.Default.Send(new NavigationMessage("ViewToNavigate"));
}

Наконец, базовый класс представления выглядит так:

public class BasePage : PhoneApplicationPage
{
    public BasePage()
    {
        Messenger.Default.Register<NavigationMessage>(this, NavigateToPage);
    }

    protected void NavigateToPage(NavigationMessage message)
    {
        //GetQueryString isn't shown, but is simply a helper method for formatting the query string from the dictionary
        string queryStringParams = message.QueryStringParams == null ? "" : GetQueryString(message);

        string uri = string.Format("/Views/{0}.xaml{1}", message.PageName, queryStringParams);
        NavigationService.Navigate(new Uri(uri, UriKind.Relative));
    }
}

Предполагается, что все представления находятся в папке "Views" в корне приложения. Это прекрасно работает для нашего приложения, но, конечно, его можно расширить для поддержки различных сценариев организации ваших представлений.

Ответ 2

Предупреждение: самоуверенный MVVM newbie alert:) (Я очень новичок в MVVM, но наслаждаюсь им до сих пор.)

Хороший вопрос. Я обнаружил, что это совершенно возможно (если немного уродливо в местах), чтобы издеваться над NavigationService и передать INavigationService в ViewModel. Фактически, вы даже можете сделать интерфейс немного приятнее с generics, передать тип (как аргумент типа), а не строковый URI.

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

Итак, это не идеальное решение, но, по крайней мере, ViewModel может нести ответственность за действие "перейти сейчас" (или "вернуться" ).