Search code examples
c#constructornavigationwindows-phone-8.1lifecycle

How to preserve data context on navigation event?


I've set up a property which triggers a custom navigation event (NavigateTo) when it's setter is called. So far the navigation works correctly on the first try, i.e an item is selected from the list view where the selected item is bound to the property, then the property setter fires and in turn the navigation event triggers.

But when I navigate back to the MainPage and select an item from the list view a second time the navigation event doesn't fire.

I've debugged the call of 'NavigateTo' which isn't called the second time I navigate back to the MainPage and select from the list view. This points out to me that the page's constructor hasn't been called and the data context to the VM isn't set, which in turn means that the setter navigation event won't be called.

Question:

How can I preserve data context during the navigation lifecycle between pages?

MainViewModel - The navigation event is called in the SelectedCouncilName setter, which passes down an int param for navigation:

    private CouncilName _selectedCouncilName;
    public CouncilName SelectedCouncilName
    {
        get
        {
            return _selectedCouncilName;
        }
        set
        {
            if (_selectedCouncilName != value)
            {
                _selectedCouncilName = value;
                RaisePropertyChanged("SelectedCouncilName");
                _navCallBack.NavigateTo(_selectedCouncilName.ID);            
            }
        }

    }

MainPage.xaml list view - the selected item is bound to the SelectedCouncilName property:

<ListView x:Name="ZonesListView"
                      Margin="0,0,-12,0"
                      ItemsSource="{Binding CouncilNameItems}"
                      SelectedItem="{Binding SelectedCouncilName,
                                             Mode=TwoWay}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0,0,0,17">
                            <TextBlock Style="{ThemeResource ListViewItemTextBlockStyle}"
                                       Text="{Binding CouncilAcronym}"
                                       TextWrapping="Wrap" />
                            <TextBlock Margin="12,-6,12,0"
                                       Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}"
                                       Text="{Binding CouncilFullName}"
                                       TextWrapping="Wrap" />
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

MainPage.xaml.cs - The code behind where the navigation event is fired following the setter being triggered on the SelectedCouncilName property. (I've included the whole page code behind to show details of how the data context is set and the navigation method):

public sealed partial class MainPage : Page, INavigationCallback
{
    MainViewModel vm;

    private NavigationHelper navigationHelper;

    public MainPage()
    {

        this.navigationHelper = new NavigationHelper(this);
        this.navigationHelper.LoadState += navigationHelper_LoadState;
        this.navigationHelper.SaveState += navigationHelper_SaveState;

        //init data context
        this.NavigationCacheMode = NavigationCacheMode.Required;
        this.InitializeComponent();

        vm = new MainViewModel(this);
        this.DataContext = vm;
        vm.LoadCouncilNamesData();
    }

    private void navigationHelper_SaveState(object sender, SaveStateEventArgs e)
    {
        throw new NotImplementedException();
    }

    void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
    {
        throw new NotImplementedException();
    }

    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached.
    /// This parameter is typically used to configure the page.</param>
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {


        // TODO: Prepare page for display here.

        // TODO: If your application contains multiple pages, ensure that you are
        // handling the hardware Back button by registering for the
        // Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
        // If you are using the NavigationHelper provided by some templates,
        // this event is handled for you.

    }

    void INavigationCallback.NavigateTo(string ItemID)
    {
        Frame.Navigate(typeof(RequestTagPage), ItemID);
    }
}

Solution

  • Remove this.NavigationCacheMode = NavigationCacheMode.Required For constructor to getting called when navigated back