I have been using the NavigationService
's Navigate
method to navigate to other pages in my WP7 Silverlight app:
NavigationService.Navigate(new Uri("/Somepage.xaml?val=dreas", UriKind.Relative));
From Somepage.xaml
, I then retrieve the query stri开发者_开发知识库ng parameters as follows:
string val;
NavigationContext.QueryString.TryGetValue("val", out val);
I now need a way to pass a complex object using a similar manner. How can I do this without having to serialize the object every time I need to pass it to a new page?
This is an extremely complex problem, and there is no simple solution here. There is no magic API that would just work for any app to solve this problem.
The core problem with passing navigation data is Tombstoning. The only piece of data that is tombstoned by default is the Navigation URI. so if you're using a QueryString parameter, it'll get picked up automatically by tombstoning and your code. Any time you'll manually pass a instance of an object though, you'll have to manually do tombstoning for that instance yourself.
So, if you navigate to "/CowDetails.xaml?ID=1" your page will probably have perfect tombstoning just by picking up on the ID Querystring Parameter. However, if you somehow provide CowDetails page with a "new Cow() { ID = 1}" you'll have to make sure to tombstone and zombificate this value yourself.
Also, there's the issue of timing. While calling NavigationService.Navigate, the page you're navigating too doesn't have an actual instance yet. So even though you're navigating to FooPage and have the FooData ready, there's no way to immediately connect FooPage to FooData. You'll have to wait until the PhoneApplicationFrame.Navigated event has fired in order to provide FooPage with FooData.
The way I normally deal with this is problem:
- Have a BasePage with an Object type Data property
- Have a NavigationHelper get the page URI and Data: NavigationHelper.Navigate("foo.xaml", fooData)
- Have NavigationHelper register to PhoneApplicationFrame.Navigated event and if it's "foo.xaml" set the BasePage.Data to FooData.
- Have BasePage use JSON.Net to tombstone and zombificate BasePage.Data.
- On BasePage, I've got a OnDataSet virtual method that is invoked once the Data property is populated either by Zombification or Navigation. In this method, everything that has to do with business data happens.
App.xaml.cs -> App class, add a field/property there. To access it, if it is static use:
App.MyComplexObject
Or if is not staic
(App.Current as App).MyComplexObject;
There's a very simple solution to tackle this problem. Consider the following example A windows phone app has following two pages, Page1.xaml and Page2.xaml Lets say from Page1.xaml we are navigating to Page2.xaml. The navigation cycle starts, when you call NavigationService.Navigate method
- First OnNavigatingFrom Event of Page1 fires
- Then Constructor of Page2 fires
- Then OnNavigatedFrom event of Page1 fires with the reference of the created page in its EventArgs (e.Content has the created instance of Page2)
- Finally OnNavigatedTo Event of Page2 fires
So we are getting the other page's reference in the page where the navigation starts.
public class PhoneApplicationBasePage : PhoneApplicationPage
{
private object navParam = null;
protected object Parameter{get;private set;}
//use this function to start the navigation and send the object that you want to pass
//to the next page
protected void Navigate(string url, object paramter = null)
{
navParam = paramter;
this.NavigationService.Navigate(new Uri(url, UriKind.Relative));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
//e.Content has the reference of the next created page
if (e.Content is PhoneApplicationBasePage )
{
PhoneApplicationBasePage page = e.Content as PhoneApplicationBasePage;
if (page != null)
{ page.SendParameter(navParam); navParam=null;}
}
}
private void SendParameter(object param)
{
if (this.Parameter == null)
{
this.Parameter = param;
this.OnParameterReceived();
}
}
protected virtual void OnParameterReceived()
{
//Override this method in you page. and access the **Parameter** property that
// has the object sent from previous page
}
}
So in our Page1.xaml.cs we simply call Navigate("/Page2.xaml",myComplexObject)
. And in your Page2.xaml.cs we will override OnParameterReceived method
protected override void OnParameterReceived()
{
var myComplexObjext = this.Parameter;
}
And it is also possible to handle tombstone problems with little more tweaks in PhoneApplicationBasePage
Debatable solution, if anything, make it temporary.
Create this under the namespace of your application for whichever page necessary.
public static class VarsForPages {
// Be sure to include public static.
public static SomeClass SomeClassObject;
public static List<string> SomeList = new List<string>();
public static string SomeData = "SomeValue";
}
// The syntax for referencing the data
VarsForPages.SomeClassObject;
VarsForPages.SomeList;
VarsForPages.SomeData;
Now you can reference SomeClassObject, SomeList, SomeData anywhere in the application.
Note: Like any global data, be weary of the many accesses & modifications which may be done to the global data. I say this, because I once had a list increase in size, but one of my pages in the application relied on the size of the list to be of some value, and this caused a bug. Don't forget, the data is global.
I wish I could reply to vjsrinath's response above; it is IMO the best way to do this. Many thanks!!
It is probably the closest thing I've seen to how the iOS model works, where from first page you called performSegue (== NavigateTo). Then you get a callback called prepareForSegue, which allows you to set up variables in the destination page, set the delegate (usually to self), that sort of thing.
For complex object passing, it beats the pants off passing params in the URL.
As an explicit example, say I want to pass the version string of my app into an About box which is in a separate project:
In the calling class:
private void About_Click(object sender, EventArgs e)
{
NavigationService.Navigate(new Uri("/Library;component/Pages/About.xaml", UriKind.Relative));
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
if (e.Content is About)
{
About page = e.Content as About;
if (page != null)
{
page.VersionString = App.VersionText;
}
}
base.OnNavigatedFrom(e);
}
In the About class:
public partial class About : PhoneApplicationPage
{
public string VersionString { get; set; }
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
versionTextBlock.Text = VersionString;
}
}
This is a very simple example of course, but the property you're passing through could be any object, making this very powerful. Of course it can include callbacks such as "saveButtonPressed" etc so the save handling can be done in the calling class, not the presented view, which is pretty crummy for code tidiness. e.g.,
page.OnSaveButtonPressed = this.SaveButtonPressedHandler; // Pass object to save as parameter
精彩评论