Обработка событий OnNavigatedFrom/OnNavigatedTo в ViewModel
Я пытаюсь выяснить способ, которым мой ViewModel обрабатывает сохранение или восстановление состояния страницы, когда страница перемещается с или на.
Первое, что я попробовал, это добавить поведение EventToCommand на страницу, но события (OnNavigatedFrom и OnNavigatedTo) объявлены защищенными, а EventToCommand не видит привязки к событиям.
Далее я подумал, что попробую использовать класс Messenger для передачи сообщения в ViewModel с использованием кода в коде просмотра позади:
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
Messenger.Default.Send<PhoneApplicationPage>(this);
base.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Messenger.Default.Send<PhoneApplicationPage>(this);
base.OnNavigatedTo(e);
}
Но у этого, похоже, есть две проблемы: сначала этот код находится в коде за страницей. Во-вторых, ViewModel не может отличить события OnNavigatedFrom и OnNavigatedTo, не создавая набор классов-оболочек для объекта PhoneApplicationPage (см. UPDATE ниже).
Каков наилучший способ MVVM-Light для обработки этих событий?
UPDATE:
Мне удалось решить вторую проблему, отправив такие сообщения:
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
Messenger.Default.Send<PhoneApplicationPage>(this,"NavigatedFrom");
base.OnNavigatedFrom(e);
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Messenger.Default.Send<PhoneApplicationPage>(this, "NavigatedTo");
base.OnNavigatedTo(e);
}
и зарегистрировать их следующим образом:
Messenger.Default.Register<PhoneApplicationPage>(this, "NavigatedFrom", false, (action) => SaveState(action));
Messenger.Default.Register<PhoneApplicationPage>(this, "NavigatedTo", false, (action) => RestoreState(action));
Ответы
Ответ 1
Выполнение команды из кода позади намного чище, чем перехват всего беспорядка сообщений. В конце концов, нет ничего плохого в представлении о его DataContext.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
viewModel.NavigatedToCommand.Execute(e.Uri);
}
ProfileViewModel viewModel
{
get
{
return this.DataContext as ProfileViewModel;
}
}
Обновление: Передача в NavigationContext.QueryString, вероятно, более полезна, поскольку она уже анализирует параметры и значение.
Ответ 2
Извините за то, что на этот вопрос три года. Да, я все еще использую Silverlight. Хорошо, я хочу записать его в Page
code-behind вот так:
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.HandleOnNavigatedTo(e);
}
Я использую метод расширения следующим образом:
public static void HandleOnNavigatedTo(this Page page, NavigationEventArgs e)
{
var vm = page.DataContext as IPageNavigationViewModel;
if (vm == null) return;
vm.HandleOnNavigatedTo(e);
}
Метод расширения подразумевает, что Page
должен иметь модель просмотра, которая реализует IPageNavigationViewModel в DataContext
. Для меня это компромисс между разделяющими факторами, когда страница знает только о наиболее общих типах данных в Домене. Это интерфейс:
using System.Windows.Navigation;
namespace Fox.Silverlight.ViewModels
{
/// <summary>
/// Defines View Model members for frame-navigation pages.
/// </summary>
public interface IPageNavigationViewModel
{
/// <summary>
/// Handles the <see cref="Page.OnNavigatedTo"/> method in the View Model.
/// </summary>
/// <param name="e">The <see cref="NavigationEventArgs"/> instance containing the event data.</param>
void HandleOnNavigatedTo(NavigationEventArgs e);
/// <summary>
/// Handles the <see cref="Page.OnNavigatedFrom"/> method in the View Model.
/// </summary>
/// <param name="e">The <see cref="NavigationEventArgs"/> instance containing the event data.</param>
void HandleOnNavigatedFrom(NavigationEventArgs e);
}
}
Ответ 3
Похоже, у вас уже есть решение проблемы. Я также предлагаю следующее:
Посмотрите на использование одного из значений сообщений, представленных в mvvm-toolkit, например:
NotificationMessage<T>
Вот так:
Messenger.Default.Send<NotificationMessage<PhoneApplicationPage>>(
new NotificationMessage<PhoneApplicationPage>(this, "Message"));
Ответ 4
Я думаю, что Райан получал, был факт, что вы используете PhoneApplicationPage как отправляемое сообщение вместо фактического сообщения.
Вы делаете это:
Messenger.Default.Send<PhoneApplicationPage>(this);
который отправляет сообщение типа PhoneApplicationPage. Вероятно, вам не нужно отправлять весь файл PhoneApplicationPage в качестве сообщения.
Вы можете сделать некоторые сообщения для NavigatingTo/NavigatingFrom, т.е.
Messenger.Default.Send<NavigatingToMessage>(new NavigatingToMessage());
и др.
Я уверен, что есть миллион лучших способов сделать это, я просто соглашался с тем, как вы все наладили. Лично у моего класса ViewModelBase есть методы NavigatingTo/NavigatingFrom, и я переопределяю соответствующие методы в представлении и отправляю их в свою ViewModel.
Ответ 5
Я делаю образец, используя обновленный ответ внутри вопроса:
MainViewModel.xaml.cs:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
Messenger.Default.Register<PhoneApplicationPage>(this, "NavigatedTo", false, ExecuteNavigatedTo);
}
// action contains everything you want.
private void ExecuteNavigatedTo(Page page)
{
// example
bool b = page.NavigationContext.QueryString.ContainsKey("id");
}
}
MainViewModel.xaml.cs:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Messenger.Default.Send<PhoneApplicationPage>(this, "NavigatedTo");
base.OnNavigatedTo(e);
}