简体   繁体   English

在WP7 Silverlight应用程序中导航时将复杂对象传递给页面

[英]Passing a complex object to a page while navigating in a WP7 Silverlight application

I have been using the NavigationService 's Navigate method to navigate to other pages in my WP7 Silverlight app: 我一直在使用NavigationServiceNavigate方法导航到我的WP7 Silverlight应用程序中的其他页面:

NavigationService.Navigate(new Uri("/Somepage.xaml?val=dreas", UriKind.Relative));

From Somepage.xaml , I then retrieve the query string parameters as follows: Somepage.xaml ,然后我检索查询字符串参数,如下所示:

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. 没有神奇的API可以用于任何应用程序来解决这个问题。

The core problem with passing navigation data is Tombstoning . 传递导航数据的核心问题是墓碑 The only piece of data that is tombstoned by default is the Navigation URI. 默认情况下逻辑删除的唯一数据是导航URI。 so if you're using a QueryString parameter, it'll get picked up automatically by tombstoning and your code. 因此,如果您使用的是QueryString参数,它将通过逻辑删除和您的代码自动获取。 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. 因此,如果您导航到“/CowDetails.xaml?ID=1”,您的页面可能只需要通过ID查询字符串参数就可以获得完美的逻辑删除。 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. 但是,如果你以某种方式为CowDetails页面提供了一个“new Cow(){ID = 1}”,那么你必须自己确保墓碑和zombificate这个值。

Also, there's the issue of timing . 此外,还有时间问题 While calling NavigationService.Navigate, the page you're navigating too doesn't have an actual instance yet. 在调用NavigationService.Navigate时,您正在导航的页面也没有实际的实例。 So even though you're navigating to FooPage and have the FooData ready, there's no way to immediately connect FooPage to FooData. 因此,即使您正在导航到FooPage并准备好FooData,也无法立即将FooPage连接到FooData。 You'll have to wait until the PhoneApplicationFrame.Navigated event has fired in order to provide FooPage with FooData. 您必须等到PhoneApplicationFrame.Navigated事件被触发才能为FooPage提供FooData。

The way I normally deal with this is problem: 我通常处理这个问题的方法是:

  1. Have a BasePage with an Object type Data property 具有对象类型数据属性的BasePage
  2. Have a NavigationHelper get the page URI and Data: NavigationHelper.Navigate("foo.xaml", fooData) 让NavigationHelper获取页面URI和数据:NavigationHelper.Navigate(“foo.xaml”,fooData)
  3. Have NavigationHelper register to PhoneApplicationFrame.Navigated event and if it's "foo.xaml" set the BasePage.Data to FooData. 让NavigationHelper注册到PhoneApplicationFrame.Navigated事件,如果它是“foo.xaml”,则将BasePage.Data设置为FooData。
  4. Have BasePage use JSON.Net to tombstone and zombificate BasePage.Data. 让BasePage使用JSON.Net到tombstone和zombificate BasePage.Data。
  5. On BasePage, I've got a OnDataSet virtual method that is invoked once the Data property is populated either by Zombification or Navigation. 在BasePage上,我有一个OnDataSet虚拟方法,一旦通过Zombification或Navigation填充Data属性就会调用它。 In this method, everything that has to do with business data happens. 在这种方法中,所有与业务数据有关的事情都会发生。

App.xaml.cs -> App class, add a field/property there. App.xaml.cs - > App类,在那里添加一个字段/属性。 To access it, if it is static use: 要访问它,如果是静态使用:

App.MyComplexObject

Or if is not staic 或者如果不是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 . 请考虑以下示例Windows Phone应用程序有以下两个页面, Page1.xamlPage2.xaml让我们从Page1.xaml说我们正在导航到Page2.xaml The navigation cycle starts, when you call NavigationService.Navigate method 当您调用NavigationService.Navigate方法时,导航循环开始

  1. First OnNavigatingFrom Event of Page1 fires 第一次OnNavigatingFrom Page1事件发生
  2. Then Constructor of Page2 fires 然后Page2的 构造函数触发
  3. Then OnNavigatedFrom event of Page1 fires with the reference of the created page in its EventArgs ( e.Content has the created instance of Page2 ) 然后OnNavigatedFrom Page1的事件触发其EventArgs中创建的页面的引用( e.Content具有创建的Page2实例)
  4. Finally OnNavigatedTo Event of Page2 fires 最后OnNavigatedTo Page2事件发生了

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) . 所以在我们的Page1.xaml.cs中,我们只需调用Navigate("/Page2.xaml",myComplexObject) And in your Page2.xaml.cs we will override OnParameterReceived method 在你的Page2.xaml.cs中,我们将覆盖OnParameterReceived方法

 protected override void OnParameterReceived()
{
var myComplexObjext = this.Parameter;
}

And it is also possible to handle tombstone problems with little more tweaks in PhoneApplicationBasePage 而且,通过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. 现在,您可以在应用程序的任何位置引用SomeClassObject,SomeList,SomeData。

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; 我希望我能回复vjsrinath上面的回复; it is IMO the best way to do this. IMO是实现这一目标的最佳方式。 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). 这可能是我见过的最接近iOS模型如何工作的东西,从第一页开始你调用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. 然后你会得到一个名为prepareForSegue的回调,它允许你在目标页面中设置变量,设置委托(通常是自己),那种东西。

For complex object passing, it beats the pants off passing params in the URL. 对于复杂的对象传递,它会在URL中传递params。

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: 作为一个明确的例子,假设我想将我的应用程序的版本字符串传递到一个单独的项目中的About框中:

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: 在About类中:

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. 当然它可以包括回调,例如“saveButtonPressed”等,因此保存处理可以在调用类中完成,而不是呈现的视图,这对于代码整洁而言非常糟糕。 eg, 例如,

page.OnSaveButtonPressed = this.SaveButtonPressedHandler; // Pass object to save as parameter

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM