locked
Page navigation in Windows Store apps RRS feed

  • Question

  • I have an application which presents a ListView of Customers. When a Customer is selected, a new page is presented with details about that customer. This page has a back button, and when it is clicked the user is returned to the ListView page. I notice that when a Customer is selected in the ListView, the page constructor for the detail page is always executed. There is quite a bit of page setup involved (like loading ComboBoxes with choices), so I would like to load the page and retain it from invocation to invocation. When I set the detail page's NavigationCacheMode to Enabled, the data doesn't change based on the Customer selected. Even with the data for the Customer being retrieved in the OnNavigatedTo event, for some reason it doesn't change. How can I keep the detail page initialization from being repeatedly executed while loading different Customer detail data each time a different Customer is selected from the ListView?
    Saturday, April 20, 2013 4:33 PM

Answers

  • Okay, a possibly dumb question, but why are you dealing with the SuspensionManager here? I'm not saying it's wrong, but I've never seen that done anywhere but in OnLaunched and OnSuspending in app.xaml.cs/vb.

    I don't know this, but that might be why NavigationMode isn't returning the correct value.


    Rebecca M. Riordan

    Saturday, April 27, 2013 10:28 AM
  • As I think you noticed, setting NavigationCacheMode to either Enabled or Required will recycle your DetailsPage object so you will avoid the constructor running each time. However, you will still have OnNavigatedTo called every time the user navigates to the page even when reusing the same page object...which I think is what you want since the selected item might be different each time and you need to change DataContext to match the newly selected item.

    The OnNavigatedTo method you posted is copied from LayoutAwarePage with the one additional line that sets DataContext. Even with your change, it doesn't seem to me like the method is exactly right for your scenario. I think the first couple of lines might not behave as you would like:

    // Returning to a cached page through navigation shouldn't trigger state loading
    if (this._pageKey != null) return;
    

    With NavigationCacheMode set to Enabled, your DetailsPage object gets reused, so the _pageKey will not be null except for the first navigation. Are you hitting that "return" statement above on subsequent navigations to your DetailsPage?

    I wonder if it might be cleaner to remove your change to LayoutAwarePage.OnNavigatedTo and just override OnNavigatedTo in your DetailsPage class and set DataContext in your override? If you go this route, you should probably call base.OnNavigatedTo so the LayoutAwarePage version runs as well. The classic thing to do is pass Customer ID of the selected item from your ListView as the navigation parameter to your DetailsPage - then in your DetailsPage.OnNavigatedTo use the ID to look up the Customer and set it as DataContext. You can look at the GridApp app template code to see an example of this style. This integrates nicely with Suspend/Terminate/Restore since saving the frame's navigation stack automatically saves the navigation parameters (which your technique of passing context in App.SelectedHouseholdMember might not).


    Saturday, April 27, 2013 2:11 PM

All replies

  • Have you tried updating the data in OnLoad instead of OnNavigated? I don't know that that will make a difference, but the VisualTree isn't guaranteed to be available in OnNavigated, and that might be what's going on.

    Rebecca M. Riordan


    Sunday, April 21, 2013 11:21 AM
  • Do you mean update the data for the Customer selected or update the data for the form, such as ComboBox choice items?
    Sunday, April 21, 2013 4:05 PM
  • Tried loading the Customer data in the page's Loaded event and it did not work. Nothing showed up except the ComboBox choices loaded in the page initiator. Looks like NavigationCacheMode must be disabled.
    Monday, April 22, 2013 7:17 AM
  • Can you show us your original OnNavigatedTo() method? It really seems like that ought to work, so maybe we're chasing the wrong problem.

    Rebecca M. Riordan

    Monday, April 22, 2013 9:47 AM
  • protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                // Returning to a cached page through navigation shouldn't trigger state loading
                if (this._pageKey != null) return;

                var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
                this._pageKey = "Page-" + this.Frame.BackStackDepth;

                if (e.NavigationMode == NavigationMode.New)
                {
                    // Clear existing state for forward navigation when adding a new page to the
                    // navigation stack
                    var nextPageKey = this._pageKey;
                    int nextPageIndex = this.Frame.BackStackDepth;
                    while (frameState.Remove(nextPageKey))
                    {
                        nextPageIndex++;
                        nextPageKey = "Page-" + nextPageIndex;
                    }

                    // Pass the navigation parameter to the new page
                    this.LoadState(e.Parameter, null);
                }
                else
                {
                    // Pass the navigation parameter and preserved page state to the page, using
                    // the same strategy for loading suspended state and recreating pages discarded
                    // from cache
                    this.LoadState(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey]);
                }
                this.pageRoot.DataContext = App.SelectedHouseholdMember;
            }

    In this scenario, with NavigationCacheMode enabled, the first HouseholdMember selected is displayed, but then remains when others are selected.

    Monday, April 22, 2013 6:17 PM
  • Tried the above code with NavigationCacheMode enabled and it works -- the correct HouseholdMember is shown when selected from the ListView, but the page initiator code is executed every time the page is accessed, so the initialization code is executed repeatedly.
    Monday, April 22, 2013 6:24 PM
  • Okay, a possibly dumb question, but why are you dealing with the SuspensionManager here? I'm not saying it's wrong, but I've never seen that done anywhere but in OnLaunched and OnSuspending in app.xaml.cs/vb.

    I don't know this, but that might be why NavigationMode isn't returning the correct value.


    Rebecca M. Riordan

    Saturday, April 27, 2013 10:28 AM
  • As I think you noticed, setting NavigationCacheMode to either Enabled or Required will recycle your DetailsPage object so you will avoid the constructor running each time. However, you will still have OnNavigatedTo called every time the user navigates to the page even when reusing the same page object...which I think is what you want since the selected item might be different each time and you need to change DataContext to match the newly selected item.

    The OnNavigatedTo method you posted is copied from LayoutAwarePage with the one additional line that sets DataContext. Even with your change, it doesn't seem to me like the method is exactly right for your scenario. I think the first couple of lines might not behave as you would like:

    // Returning to a cached page through navigation shouldn't trigger state loading
    if (this._pageKey != null) return;
    

    With NavigationCacheMode set to Enabled, your DetailsPage object gets reused, so the _pageKey will not be null except for the first navigation. Are you hitting that "return" statement above on subsequent navigations to your DetailsPage?

    I wonder if it might be cleaner to remove your change to LayoutAwarePage.OnNavigatedTo and just override OnNavigatedTo in your DetailsPage class and set DataContext in your override? If you go this route, you should probably call base.OnNavigatedTo so the LayoutAwarePage version runs as well. The classic thing to do is pass Customer ID of the selected item from your ListView as the navigation parameter to your DetailsPage - then in your DetailsPage.OnNavigatedTo use the ID to look up the Customer and set it as DataContext. You can look at the GridApp app template code to see an example of this style. This integrates nicely with Suspend/Terminate/Restore since saving the frame's navigation stack automatically saves the navigation parameters (which your technique of passing context in App.SelectedHouseholdMember might not).


    Saturday, April 27, 2013 2:11 PM