Navigatie op binnen een WP7 applicatie
Tijdens het ontwikkelen van een van m’n WP7 applicaties liep ik er tegenaan dat er naar een andere pagina moest worden genavigeerd. Je kunt, naar mijn mening, namelijk niet alles binnen een Pivot- of Panorama-control plaatsen. Het kan ook zijn dat dit nog een ouderwetse gedachtengang is, maar daar ben ik nog niet achter.
Het probleem was echter dat navigatie niet echt (goed) is geimplementeerd binnen WP7 of Silverlight. Omdat dit toch moest binnen mijn applicatie heb ik hier een oplossing voor moeten verzinnen. Gelukkig was ik niet de enige met dit probleem en kwam ik op Stack Overflow (link ben ik kwijt) al snel op een antwoord waar ik mee kon werken.
Het idee is dat je je views laat afleiden van een BaseView
. In deze BaseView
definieer je dan de navigatie methoden, zodat alle views dit kunnen doen. De navigatie wordt gedaan aan de hand van een navigatie object en het registreren op de wijzigingen binnen dit object. Door gebruik te maken van de Message bus die je gratis bij MVVM Light krijgt is dit eenvoudig te realiseren.
Eerst dient er een object te worden gemaakt waar de pagina en querystring in kan worden gestopt. Dit is bij mij een nieuwe klasse welke er als volgt uit ziet:
public class NavigationMessage : NotificationMessage
{
public string PageName
{
get { return base.Notification; }
}
public Dictionary QueryStringParams { get; private set; }
public NavigationMessage(string pageName) : base(pageName) { }
public NavigationMessage(string pageName, Dictionary queryStringParams)
: this(pageName)
{
QueryStringParams = queryStringParams;
}
}
Zodra deze is gemaakt kan er een BaseView worden aangemaakt waarin deze zichzelf registreert op wijzigingen van objecten van de vooraf gedefinieerde klasse. Bij een wijziging kan er dan een methode worden aangeroepen welke de navigatie tot stand brengt. Dit zal er ongeveer als volgt uit komen te zien:
public class BaseView : PhoneApplicationPage
{
public BaseView()
{
Messenger.Default.Register(this, NavigateToPage);
}
public void NavigateToPage(NavigationMessage message)
{
string queryStringParams = message.QueryStringParams == null ? "" : GetQueryString(message);
string uri = string.Format("/{0}.xaml{1}", message.PageName, queryStringParams);
NavigationService.Navigate(new Uri(uri, UriKind.Relative));
}
private string GetQueryString(NavigationMessage message)
{
string queryString = string.Empty;
bool first = true;
foreach (var s in message.QueryStringParams)
{
if (first)
{
queryString += "?";
first = false;
}
queryString += string.Format("{0}={1}", s.Key, s.Value);
}
return queryString;
}
}
Wat wel belangrijk is om te weten is dat de XAML en de code-behind hiervan beide moeten afleiden van de BaseView. Dit was waar ik de fout in ging, aangezien ik niet wist hoe je in XAML zoiets kunt bewerkstelligen. Gelukkig is dit heel eenvoudig. De code-behind gaat zoals je dat normaal doet:
public partial class MainPage : BaseView
In de XAML is dit iets uitgebreider. Daar moet er namelijk een namespace worden toegevoegd en het root element moet worden gewijzigd in de juiste BaseView klasse.
Wanneer je dit niet doet krijg je de melding dat een partial klasse niet van verschillende base klassen kan overerven. Logisch!
Zodra alles is opgezet en geregistreerd kun je, dankzij de Messenger bus, met een regel een andere pagina aanroepen:
Messenger.Default.Send(new NavigationMessage("Results"));
Op zich is het allemaal heel eenvoudig, maar je moet er wel even aan denken. Wat ik wel een eye-opener vond is dat Silverlight dus blijkbaar ook werkt met Uri’s en querystrings, net zoals een web applicatie. Dit is waarschijnlijk triviale kennis voor een Silverlight developer, waardoor wel weer blijkt dat ik nog een beginner ben op dit vlak.