I'm looking for a way to navigate between screens in my app. Basically what I've seen so far consists of passing a string URI to the NavigationService, complete with query string parameters., e.g.
NavigationService.Navigate(new Uri("/MainPage.xaml?selectedItem=" +bookName.Id, UriKind.Relative));
I'm not really keen on this though ultimately because it requires magic strings, and they can lead to problems down the road.
Ideally I'd just create an instance of the class I want to navigate to, passing the parameters as arguments to the constructor. Is this possible? If so, how?
While the actual navigation will have to use strings eventually, you can create or use a wrapper that is type safe.
I would suggest looking at Caliburn Micro even if you only used it for the type safe navigation. Here is a snippet from a tutorial on using it in WP8:
The NavigationService that comes with the toolkit supports a view model first approach: instead of declaring which is the URL of the page where we want to take the user (that is the standard approach), we declare which is the ViewModel we want to display. The service will take care of creating the correct URL and display the view that is associated with the view model.
Alternatively you could look at Windows Phone MVC which also has some type safe navigation. You might even just be able to pull the navigation code out to use on your own since it's licensed under MS-PL.
Basically, no, not that's built-in. Complex parameters like IRepository instances are, unfortunately, beyond the ability of the navigation facilities in Silverlight; I usually use some form of IoC container to handle those. Simpler POCO parameters are easily serialized to a string, but that still requires magic strings and manual query-string parsing.
You can, however, easily build something typesafe yourself. For example, here's my approach.
For parameter data, I have a class that I call 'Extras', which wraps a Dictionary<string, object>
with methods like GetBool(string)
, GetInt32(string)
, etc., and has a static factory method CreateFromUri(Uri)
; this is good enough for my purposes.
I use this in conjunction with type-safe navigation. I really like the MVVM pattern, and each of my pages has a ViewModel encapsulating nearly all logic. The one-to-one relationship of page to ViewModel makes the latter an ideal navigation key. That, combined with attributes and reflection, gives us a simple solution:
public class NavigationTargetAttribute : Attribute
{
private readonly Type target;
public ViewModelBase Target
{
get { return target; }
}
public NavigationTargetAttribute(Type target)
{
this.target = target;
}
}
Put one of these on each of your pages, with the proper ViewModel type.
[NavigationTarget(typeof(LoginViewModel))]
public class LoginPage : PhoneApplicationPage
{ ... }
Then, in a singleton NavigationManager-esque class, you can do:
GetType().Assembly
.GetTypes()
.Select(t => new { Type = t, Attr = t.GetCustomAttributes(false).FirstOrDefault(attr => attr is NavigationTargetAttribute) })
.Where(t => t.Attr != null);
And just like that, you have a collection of every navigable type in your app. From there, it's not much more work to put them in a dictionary, for example. If you follow a convention for where you put your pages, you can (for example) translate between type and Uri quite easily... for example, new Uri("/Pages/" + myPageType.Name + ".xaml", UriKind.Relative)
. It's not much more to add support for query parameters. Finally, you'll end up with a method, like so:
public void Navigate(Type target, Extras extras)
{
Type pageType;
if (navigationTargets.TryGetValue(target, out pageType))
{
var uri = CreateUri(pageType, extras);
navigationService.NavigateTo(uri);
}
// error handling here
}
Finally, in the page's OnNavigatedTo
method, I do something like:
var extras = Extras.CreateFromUri(e.Uri);
((ViewModelBase) DataContext).OnNavigatedTo(extras);
This, finally, gives a semblance of strongly-typed navigation. This is a bare-bones approach; off the top of my head, this could be improved by adding required parameters in the navigation attribute and validating them at navigation-time. It also doesn't support more complex types of navigation, where the value of nav arguments would determine the ultimate destination. Nevertheless, this suits my 90% use case - maybe it will work for you, too.
There are definitely some details omitted here, like how exactly to get an instance of NavigationService
- I can work up a more complete sample later tonight, but this should be enough to get started.
You can use PhoneApplicationService.State
It is a Dictionary<String,Object>
PhoneApplicationService.State
is commonly used in tombstoning to store the current state of the application. However, it can be used to conveniently pass data between pages.
MSDN documentation
Windows Phone applications are deactivated when the user navigates to another application. When the user returns to the application, by using the Back button or by completing a Launcher or Chooser task, the application is reactivated. An application can store transient application state in the State dictionary in the handler for the Deactivated event. In the Activated event handler, an application can use the values stored in the State dictionary to transient application state.
Basically what you would do is
PhoneApplicationService.State.add(selectedName,yourobjectInstance);
NavigationService.Navigate((new Uri("/MainPage.xaml?selectedItem="+selectedName,UriKind.Relative));
Then in your navigated too method you can retrieve it
YourObject yourObjectInstance;
var yourObj = PhoneApplicationService.State["yourObjectName"];
yourObjectInstance = yourObj is YourObject ? (yourObj as YourObject) : null;
Here is a more indepth look into how to use this feature
WPF supports navigating to an already created object, but WP8 lacks that Navigate
overload.
If you don't want to hard-code XAML page URIs, you can you can use the following (a bit dirty) helper function to get the .xaml resource URI of some class.
static Uri GetComponentUri<T>() where T : DependencyObject, new() {
return BaseUriHelper.GetBaseUri(new T());
}
Then you can modify that URL and navigate to it:
var baseUri = GetComponentUri<SomePage>(); //Uri="pack://application:,,,/MyProject;component/MainWindow.xaml"
var pageUri = new UriBuilder(baseUri) { Query = "selectedItem=" + bookName.Id };
NavigationService.Navigate(pageUri);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With