Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling the OnNavigatedFrom / OnNavigatedTo events in the ViewModel

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));
like image 919
Jeff R Avatar asked Jul 27 '10 21:07

Jeff R


3 Answers

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.

like image 107
Doguhan Uluca Avatar answered Oct 17 '22 18:10

Doguhan Uluca


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);
    }
}
like image 42
rasx Avatar answered Oct 17 '22 17:10

rasx


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"));
like image 28
Ryan from Denver Avatar answered Oct 17 '22 17:10

Ryan from Denver