Передача сложного объекта на страницу во время навигации в приложении WP7 Silverlight

Я использую метод NavigationService Navigate для перехода на другие страницы моего приложения WP7 Silverlight:

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

Из Somepage.xaml я затем извлекаю параметры строки запроса следующим образом:

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

Мне теперь нужен способ передать сложный объект с помощью аналогичного метода. Как я могу сделать это без необходимости сериализации объекта каждый раз, когда мне нужно передать его на новую страницу?

Ответ 1

Это очень сложная проблема, и здесь нет простого решения. Нет волшебного API, который бы просто работал для любого приложения, чтобы решить эту проблему.

Основная проблема с прохождением навигационных данных - Tombstoning. Единственным фрагментом данных, который по умолчанию является надгробный камень, является URI навигации. поэтому, если вы используете параметр QueryString, он будет автоматически загружаться с помощью гемблинга и вашего кода. Каждый раз, когда вы вручную передаете экземпляр объекта, вам придется вручную выполнить надгробный сбор для этого экземпляра самостоятельно.

Итак, если вы перейдете в "/CowDetails.xaml?ID=1", ваша страница, вероятно, будет иметь идеальное гемблирование, просто набрав параметр ID Querystring. Однако, если вы каким-то образом предоставите страницу CowDetails с "новой Cow() {ID = 1}", вам нужно будет убедиться в том, что захоронение и зомбифицировать это значение самостоятельно.

Кроме того, существует проблема синхронизации. При вызове NavigationService.Navigate страница, которую вы просматриваете, еще не имеет фактического экземпляра. Поэтому, даже если вы переходите к FooPage и имеете FooData, нет никакого способа немедленно подключить FooPage к FooData. Вам придется подождать, пока событие PhoneApplicationFrame.Navigated не будет запущено, чтобы предоставить FooPage с помощью FooData.

Как я обычно справляюсь с этой проблемой:

  • У базы BasePage есть свойство Data type Свойство
  • Имейте NavigationHelper получить URI страницы и данные: NavigationHelper.Navigate( "foo.xaml", fooData)
  • У меня есть NavigationHelper для регистрации на событие PhoneApplicationFrame.Navigated, и если он "foo.xaml", установите BasePage.Data в FooData.
  • Если BasePage использует JSON.Net для надгробия и зомбификата BasePage.Data.
  • В BasePage у меня есть виртуальный метод OnDataSet, который вызывается, когда свойство Data заполнено либо Zombification, либо Navigation. В этом методе происходит все, что связано с бизнес-данными.

Ответ 2

App.xaml.cs → Класс приложения, добавьте в него поле/свойство. Чтобы получить доступ к нему, если это статическое использование:

App.MyComplexObject

Или, если не является staic

(App.Current as App).MyComplexObject;

Ответ 3

Там очень простое решение для решения этой проблемы. Рассмотрим следующий пример Приложение Windows Phone имеет следующие две страницы: Page1.xaml и Page2.xaml Скажем, из Page1.xaml мы переходим к Page2.xaml. Навигационный цикл начинается, когда вы вызываете метод NavigationService.Navigate

  • Первый OnNavigatingFrom Событие Page1 срабатывает
  • Затем Конструктор из Page2 срабатывает
  • Затем OnNavigatedFrom событие Page1 запускается со ссылкой на созданную страницу в EventArgs (e.Content имеет созданный экземпляр Page2)
  • Наконец 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

Ответ 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 выше; это ИМО лучший способ сделать это. Большое спасибо!

Скорее всего, это самая близкая вещь, которую я видел, как работает модель iOS, где с первой страницы вы назвали performSegue (== NavigateTo). Затем вы получаете обратный вызов под названием prepareForSegue, который позволяет вам устанавливать переменные на целевой странице, устанавливать делегат (обычно для себя), что-то вроде этого.

Для передачи сложного объекта он удаляет пропущенные параметры переноса в URL-адресе.

Как явный пример, скажем, я хочу передать строку версии своего приложения в поле "О программе", которое находится в отдельном проекте:

В вызывающем классе:

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