Handling the OnNavigatedFrom / OnNavigatedTo events in the ViewModel
Asked Answered
C

5

10

I am trying to figure out a way for my ViewModel to handle saving or restore the page's state when the page is navigated From or To.

The first thing I tried was to add an EventToCommand behavior to the page, but the events (OnNavigatedFrom and OnNavigatedTo) are declared protected and the EventToCommand does not see the events to bind to.

Next I thought I would try using the Messenger class to pass a message to the ViewModel using code in the View's code behind:

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);
}

But this seems to have two issues, first is having this code in the code behind page. Second, the ViewModel cannot tell the difference between the OnNavigatedFrom and the OnNavigatedTo events without having to create a set a wrapper classes for the PhoneApplicationPage object (see UPDATE below).

What is the most MVVM-Light friendly way to handle these events?

UPDATE: I was able to resolve the second issue by Sending the Messages like this:

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);
}

and Registering them like this:

Messenger.Default.Register<PhoneApplicationPage>(this, "NavigatedFrom", false, (action) => SaveState(action));
Messenger.Default.Register<PhoneApplicationPage>(this, "NavigatedTo", false, (action) => RestoreState(action));
Cide answered 27/7, 2010 at 21:43 Comment(0)
G
5

Executing a command from code behind is far cleaner than going through the whole messaging mess. After all there's nothing wrong with the view knowing about its 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;
        }
    }

Update: Passing in NavigationContext.QueryString is probably more useful, since it already parses out the parameters and value.

Gloria answered 1/6, 2011 at 19:27 Comment(1)
Am not agaisnt with MVVM Light. I'm just thinking about it's execution time. Going from VIEW to ViewModel through ViewModelLocatorSnowfall
S
3

Sorry for being three years late to this question. Yes, I'm still using Silverlight. Okay I want to write it in Page code-behind like this:

// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
    this.HandleOnNavigatedTo(e);
}

I am using an extension method like this:

public static void HandleOnNavigatedTo(this Page page, NavigationEventArgs e)
{
    var vm = page.DataContext as IPageNavigationViewModel;
    if (vm == null) return;
    vm.HandleOnNavigatedTo(e);
}

The extension method implies that the Page must have a View Model that implements IPageNavigationViewModel in DataContext. For me, this is a separation-of-concerns compromise where the Page knows only about the most general-purpose data types in the Domain. This the interface:

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);
    }
}
Sec answered 5/4, 2013 at 23:59 Comment(0)
T
1

Looks like you have a solution to your problem already. I would also suggest the following:

Look at using one of the message values provided in the mvvm-toolkit, such as:

    NotificationMessage<T>

Like this:

    Messenger.Default.Send<NotificationMessage<PhoneApplicationPage>>(
new NotificationMessage<PhoneApplicationPage>(this, "Message"));
Tyrannous answered 4/8, 2010 at 0:12 Comment(0)
I
0

I think what Ryan was getting at, was the fact that you're using the PhoneApplicationPage as the message that is being sent, instead of an actual message.

You're doing this:

Messenger.Default.Send<PhoneApplicationPage>(this);

which is sending a message of type PhoneApplicationPage. You probably don't need to send the entire PhoneApplicationPage as the message.

You could make some messages for NavigatingTo / NavigatingFrom, ie.

Messenger.Default.Send<NavigatingToMessage>(new NavigatingToMessage());

etc.

I'm sure there are a million better ways to do this, I was just going along with how you had set things up. Personally, my ViewModelBase class has NavigatingTo/NavigatingFrom methods and I override the respective methods in the View and send them to my ViewModel.

Iene answered 13/7, 2011 at 20:43 Comment(0)
C
0

I make a sample using the updated answer inside the question :

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);
}
Collencollenchyma answered 2/4, 2015 at 12:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.