【问题标题】:Passing a complex object to a page while navigating in a WP7 Silverlight application在 WP7 Silverlight 应用程序中导航时将复杂对象传递到页面
【发布时间】:2011-06-09 17:59:54
【问题描述】:

我一直在使用NavigationServiceNavigate 方法导航到我的 WP7 Silverlight 应用程序中的其他页面:

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

Somepage.xaml,然后我检索查询字符串参数如下:

string val;
NavigationContext.QueryString.TryGetValue("val", out val);

我现在需要一种使用类似方式传递复杂对象的方法。每次我需要将对象传递到新页面时,如何不必序列化对象

【问题讨论】:

  • 是不是不想做序列化,但不介意推卸责任?如果是这样,我听说 IsolatedStorage 已得到改进 - 有帮助吗?
  • 真正需要理解的是,当用户按下后退按钮时会发生什么?

标签: c# silverlight windows-phone-7 navigation


【解决方案1】:

这是一个极其复杂的问题,这里没有简单的解决方案。没有任何神奇的 API 可以适用于任何应用来解决这个问题。

传递导航数据的核心问题是墓碑化。默认情况下,唯一被墓碑化的数据是导航 URI。因此,如果您使用的是 QueryString 参数,它将被墓碑和您的代码自动拾取。但是,任何时候您手动传递对象的实例时,您都必须自己手动为该实例执行墓碑。

因此,如果您导航到“/CowDetails.xaml?ID=1”,您的页面可能只需选择 ID Querystring 参数即可拥有完美的墓碑。但是,如果您以某种方式为 CowDetails 页面提供“new Cow() { ID = 1}”,则必须确保自己墓碑化并僵尸化此值。

还有时间问题。在调用 NavigationService.Navigate 时,您正在导航的页面还没有实际实例。因此,即使您正在导航到 FooPage 并准备好 FooData,也无法立即将 FooPage 连接到 FooData。您必须等到 PhoneApplicationFrame.Navigated 事件触发后才能为 FooPage 提供 FooData。

我通常处理这个问题的方式是:

  1. 拥有一个带有 Object 类型 Data 属性的 BasePage
  2. 让 NavigationHelper 获取页面 URI 和数据:NavigationHelper.Navigate("foo.xaml", fooData)
  3. 让 NavigationHelper 注册到 PhoneApplicationFrame.Navigated 事件,如果它是“foo.xaml”,则将 BasePage.Data 设置为 FooData。
  4. 让 BasePage 使用 JSON.Net 来墓碑和僵尸化 BasePage.Data。
  5. 在 BasePage 上,我有一个 OnDataSet 虚拟方法,一旦通过 Zombification 或 Navigation 填充 Data 属性,就会调用该方法。在这种方法中,与业务数据有关的一切都会发生。

【讨论】:

    【解决方案2】:

    App.xaml.cs -> App 类,在那里添加一个字段/属性。要访问它,如果是静态使用:

    App.MyComplexObject
    

    或者如果不是静态的

    (App.Current as App).MyComplexObject;
    

    【讨论】:

      【解决方案3】:

      有一个非常简单的解决方案可以解决这个问题。考虑以下示例 一个 windows phone 应用程序有以下两个页面,Page1.xamlPage2.xaml 假设我们从 Page1.xaml 导航到 Page2.xaml。当您调用 NavigationService.Navigate 方法

      时,导航周期开始
      1. Page1 触发的第一个 OnNavigatingFrom 事件
      2. 然后 Page2Constructor 触发
      3. 然后 Page1OnNavigatedFrom 事件触发,并在其 EventArgs 中引用创建的页面(e.Content 具有创建的实例 第 2 页)
      4. 最后 OnNavigatedTo Page2 事件触发

      所以我们在导航开始的页面中获取其他页面的引用。

      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
      }
      }
      

      所以在我们的 Page1.xaml.cs 中,我们只需调用 Navigate("/Page2.xaml",myComplexObject)。在您的 Page2.xaml.cs 中,我们将覆盖 OnParameterReceived 方法

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

      还可以通过在 PhoneApplicationBasePage

      中进行更多调整来处理墓碑问题

      【讨论】:

      • 嘿,我是 windows phone 开发的新手。我了解您的解决方案并喜欢它。但是当我用我的 BaseClass 扩展 Page.xaml.cs 时,它给了我运行时异常,即部分类必须扩展相同的基类。您能否建议我如何创建扩展 PhoneApplicationPage 类的 BaseClass,然后 Page1.xaml.cs 将扩展 BaseClass。
      • 'PhoneApplicationBasePage' 的命名空间引用添加到 xaml。例如,如果您的类在命名空间 "Sample" 下,则可以像这样 'xmlns:sample="clr-namespace:Sample' 将引用添加到 xaml。在如果您在单独的程序集“SampleAssembly”中创建了此类,那么您在 xaml 中的引用应该是 'xmlns:sample="clr-namespace:Sample;assembly:SampleAssembly' 然后替换 '带有 'sample:PhoneApplicationBasePage' 的 phone:PhoneApplicationPage' 标记。你后面代码的基类和xaml中定义的应该是一样的。
      【解决方案4】:

      有争议的解决方案,如果有的话,把它变成临时的。

      在您的应用程序的命名空间下为任何必要的页面创建它。

      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;
      

      现在您可以在应用程序的任何位置引用 SomeClassObject、SomeList、SomeData。

      注意:与任何全局数据一样,请注意可能对全局数据进行的许多访问和修改。我这样说是因为我曾经有一个列表增加了大小,但是我在应用程序中的一个页面依赖于列表的大小来获得一些价值,这导致了一个错误。别忘了,数据是全局的。

      【讨论】:

      • 这正是我想要的。谢谢!
      【解决方案5】:

      我希望我可以回复上面 vjsrinath 的回复;这是 IMO 最好的方法。非常感谢!!

      这可能是我所见过的最接近 iOS 模型工作原理的东西,从第一页开始,您就调用了 performSegue (== NavigateTo)。然后你会得到一个名为 prepareForSegue 的回调,它允许你在目标页面中设置变量,设置委托(通常为自身),诸如此类。

      对于复杂的对象传递,它击败了在 URL 中传递参数的裤子。

      作为一个明确的例子,假设我想将我的应用程序的版本字符串传递到一个位于单独项目中的 About 框中:

      在调用类中:

      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);
          }
      

      在 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;
          }
      }
      

      这当然是一个非常简单的例子,但是你传递的属性可以是任何对象,这使得它非常强大。当然,它可以包含诸如“saveButtonPressed”等回调,因此保存处理可以在调用类中完成,而不是呈现的视图,这对于代码整洁来说非常糟糕。例如,

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多