×
Namespaces

Variants
Actions

ViewModel backstack

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to navigate to the same page with different data while maintaining back button functionality

SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code Example
Source file: Source on GitHub-->
Tested with
SDK: Windows Phone 8.0 SDK
Devices(s): Lumia 920 Lumia 1020
Compatibility
Platform(s):
Windows Phone 8
Dependencies: Json.NET
Article
Created: NicoVermeir (27 Nov 2013)
Last edited: pavan.pareta (03 Dec 2013)

Contents

Introduction

When working on a comic application for Windows Phone I encountered a problem that I’ve had in the past and have heard others ran into as well. When on the detailspage of a comic character I have a list of enemies of that character. Those enemies are clickable to load their details. Nothing hard there, but both the first character and its enemies are of the same type and they use the same view and viewmodel to show their data. This isn’t hard to do, the difficult part is using the phone’s back button. After navigating to the same CharacterDetailPage 4 times I would expect the back button to take me back through all the characters I’ve viewed. I've created a solution for this problem and poured it into a Nuget package, hoping that I can help others to solve this problem. In this article I'll explain how to use the package. The demo application described here is included in the GitHub repository together with the package itself.

Adding the package

I'm assuming that you're using some form of MVVM, the sample project uses MVVM Light. In your Windows Phone 8 solution, use the following command in the Package Manager Console or search for ViewModelBackstack in the Nuget GUI

Install-Package ViewModelBackstack

Congratulations, you just did the hardest part of this setup!

The sample application

The sample application is a basic one, it has two pages, a MainPage and a GuidPage. The MainPage only contains some text and a button to navigate to the second page. The GuidPage contains a textblock that is bound to a property on the viewmodel, and a button that simulates navigating to the same page again but loading in different data.

The scenario is this:

MainPage > GuidPage > GuidPage > GuidPage > …

MainVM > GuidVM > GuidVM > GuidVM > …

Follow it the other way around to know how the back button will respond.

The ViewModelBackstack class

ViewModelBackstack is a static class, and not a very big one.

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
 
namespace ViewModelBackstack
{
public static class ViewModelBackStack
{
private static Dictionary<string, string> _viewModelStack;
 
public static void Add(string key, object value)
{
if (_viewModelStack == null)
_viewModelStack = new Dictionary<string, string>();
 
_viewModelStack.Add(key, JsonConvert.SerializeObject(value));
}
 
public static object Take<T>(string key)
{
string toReturn = _viewModelStack[key];
Delete(key);
 
return JsonConvert.DeserializeObject<T>(toReturn);
}
 
public static bool TryTake<T>(string key, out T value)
where T : class
{
try
{
value = JsonConvert.DeserializeObject<T>(_viewModelStack[key]);
Delete(key);
 
return true;
}
catch (Exception)
{
value = null;
return false;
}
}
 
public static bool ContainsKey(string key)
{
if (_viewModelStack == null)
return false;
 
return _viewModelStack.ContainsKey(key);
}
 
public static void Delete(string key)
{
_viewModelStack.Remove(key);
}
 
public static void Replace(string key, object newValue)
{
_viewModelStack[key] = JsonConvert.SerializeObject(newValue);
}
 
public static bool CanGoBack()
{
if (_viewModelStack == null)
return false;
 
return _viewModelStack.Count > 0;
}
 
public static T GoBack<T>()
{
var toReturn = _viewModelStack.Last();
_viewModelStack.Remove(toReturn.Key);
 
return JsonConvert.DeserializeObject<T>(toReturn.Value);
}
}
}

It contains a Dictionary<string, string> that will hold the instances of the viewmodels. The instances are serialized into JSON strings with Json.net. This to save memory and avoid reference issues. There are some methods in there to manually take out a specific instance or to delete one. But more importantly are the CanGoBack() and GoBack() methods. Let’s have a look at how to use this.

Usage

In the GuidViewModel’s constructor we start listening for a message, when that message arrives we load in new data (in this case, generate a new GUID) GuidString is a normal property that calls RaisePropertyChanged from the setter.

public GuidViewModel()
{
Messenger.Default.Register<GenerateNewGuidMessage>(this, msg => GenerateGuid());
}
 
private void GenerateGuid()
{
GuidString = Guid.NewGuid().ToString();
}

Next is the command that is bound to the button on the page, this is a RelayCommand that will call the LoadNewData method

private void LoadNewData()
{
if (ViewModelBackStack.ContainsKey(GuidString))
ViewModelBackStack.Replace(GuidString, this);
else
ViewModelBackStack.Add(GuidString, this);
 
Messenger.Default.Send(new GenerateNewGuidMessage());
}

The LoadNewData method will check if the ViewModelBackStack already contains the key we use (each instance needs a unique key, we’re using the GUID in this case). If it’s already there, replace it, if not add it to the backstack. After that, send the message to generate new data.

Note that we’re not actually navigating away from the page, since the NavigationService doesn’t actually navigate when you try going to the same page there’s really no use in trying.

The final step is intercepting the back button press and using it load in a previous instance of the GuidViewModel. We need to do this in the code-behind of the page, since we need to cancel the navigation there (by default, when pressing the back button here it would just take us back to MainPage, so navigation needs to be cancelled).

protected override void OnBackKeyPress(CancelEventArgs e)
{
if (ViewModelBackStack.CanGoBack())
{
DataContext = ViewModelBackStack.GoBack<GuidViewModel>();
e.Cancel = true;
return;
}
 
base.OnBackKeyPress(e);
}

OnBackKeyPress can be overriden from PhoneApplicationPage base class. If the ViewModelBackStack can go back we take out the most recent record in the dictionary, deserialize it to T, set that result as DataContext and we’re done. We can cancel the navigation by setting e.Cancel to true. Once the ViewModelBackStack is empty the app will return to MainPage.

Summary

In this article I've explained how you can leverage the ViewModelBackstack package on Nuget to easily navigate to the same page with different data over and over again while maintaining a correct backstack for the phone's backbutton. I've also explained the inner workings of the package.

This page was last modified on 3 December 2013, at 09:51.
139 page views in the last 30 days.