WinRT / UWP Frame e Page caching: come creare una nuova istanza di pagina su Naviga () e mantenere l’istanza di pagina su GoBack ()

Sto cercando di creare un’applicazione UWP (Universal Windows App) con C #. Il mio problema è il controllo Frame : se lo utilizzo senza NavigationCacheMode = Required , ogni volta che l’utente torna indietro, la pagina non viene mantenuta in memoria e verrà ricreata. Se imposto NavigationCacheMode su Required o Enabled , tornare indietro funziona correttamente (nessun nuovo object di pagina) ma se si passa a un’altra pagina dallo stesso tipo, l’object della pagina precedente viene riciclato e riutilizzato (nessuna nuova istanza di pagina).

Comportamento desiderato:

C’è un modo per avere il seguente comportamento con il controllo Frame originale (come in Windows Phone):

  1. Crea una nuova istanza di pagina su Navigate()
  2. Mantieni l’istanza della pagina su GoBack()

L’unica soluzione che conosco è quella di creare un proprio controllo Frame , ma questo porta ad altri problemi (ad esempio: mancante del metodo SetNavigationState() , ecc …)

Scenario di esempio:

Semplice esempio di applicazione con tre pagine: TvShowListPage , TvShowDetailsPage , SeasonDetailsPage .

  1. TvShowListPage è la pagina di ingresso. Dopo aver cliccato su un TvShow TvShowDetailsPage a TvShowDetailsPage .
  2. Ora in TvShowDetailsPage seleziona una stagione nell’elenco e TvShowDetailsPage a TvShowDetailsPage .
  3. Se si sta tornando indietro, le pagine dovrebbero rimanere in memoria per evitare di ricaricare le pagine.
  4. Ma se gli utenti tornano a TvShowListPage e selezionano un altro TvShow TvShowDetailsPage viene riciclato e TvShowDetailsPage essere in uno stato sbagliato (ad esempio, mostrando il perno di lancio invece del primo, il perno di stagioni)

Sto cercando il comportamento predefinito di Windows Phone 7: Navigazione crea una nuova pagina nello stack di pagine, tornando indietro rimuove la pagina superiore dallo stack e visualizza la pagina precedente dallo stack (memorizzata nella memoria).

Soluzione:

Perché non c’era una soluzione a questo problema, ho dovuto reimplementare tutte le classi rilevanti di paging: Page, Frame, SuspensionManager, ecc …

La libreria MyToolkit che fornisce tutte queste classi può essere scaricata qui: https://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview

Riferimenti:

  • http://www.jayway.com/2012/05/25/clearing-the-windows-8-page-cache/ : nessuna buona soluzione
  • http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/88e6d1b3-1fa6-4ab4-a816-e77c86ef236f/ : L’implementazione di una propria class Frame non è una soluzione poiché non funziona con SuspensionManager

Perché non c’era una soluzione a questo problema, ho dovuto reimplementare tutte le classi rilevanti di paging: Page, Frame, SuspensionManager, ecc …

La soluzione può essere scaricata qui: https://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview

Aggiornare:

La class di pagine ora fornisce anche il metodo OnNavigatingFromAsync per mostrare ad esempio un popup asincrono e annullare la navigazione se necessario …

Ho avuto lo stesso problema. Volevo che quando avanzavo in Metro (Windows Store fosse corretto), creava una nuova istanza. Tuttavia, quando torni indietro, manterrebbe i dati che volevo salvare.

Quindi, ho usato anche NavigationCacheMode = NavigationCacheMode.Enabled. Ho scoperto che indipendentemente dal modo in cui stavo attraversando, avanti o indietro, tutto è stato sempre salvato. Quindi, vorrei andare avanti diverse pagine, quindi fare un passo indietro. Sperando che tutto fosse resettato mentre avanzavo, ho sempre trovato che non lo era; aveva mantenuto i dati.

Ho provato di tutto, incluso scrivere il mio codice del pulsante back per includere NavigationCacheMode = NavigationCacheMode.Disabled, ma senza alcun risultato. Come altri hanno sottolineato, una volta abilitato, NavigationCacheMode semplicemente non lo disabiliterà.

Ho trovato una soluzione. Sono andato su LayoutAwarePage.cs e ho semplicemente apportato una piccola modifica. Sotto “OnNavigatedTo” ho trovato la linea:

 // Returning to a cached page through navigation shouldn't trigger state loading if (this._pageKey != null) return; 

Tuttavia, il commento è andato contro ciò che volevo. Stavo cercando il caricamento di stato in uno schema unidirezionale. Se procedendo in avanti, volevo caricare lo stato; se si muovesse all’indietro volevo il comportamento indicato dal commento – nessun caricamento dello stato.

Quindi ho semplicemente modificato la linea.

 // Returning to a cached page through navigation shouldn't trigger state loading if (this._pageKey != null && e.NavigationMode == NavigationMode.Back) return; 

L’ho provato e funziona perfettamente. Ora, navigando all’indietro, ricorda lo stato e mantiene la pagina uguale. Navigando in avanti, si carica fresco.

Forse non la migliore pratica, ma non chiamo “OnNavigatedTo” dal mio code-behind. Faccio tutto attraverso il “LoadState”. Se si sta eseguendo l’override di “OnNavigatedTo” nel code-behind, si potrebbe notare un comportamento diverso.

Grazie,

Joseph Irvine

Quando stai navigando in avanti , puoi impostare NavigationCacheMode su Disabled prima di chiamare Frame.Navigate ? Quindi, in OnNavigatedTo () imposta nuovamente NavigationCacheMode su Enabled .

Questo dovrebbe far sì che quando navighi in avanti, il caching sia disabilitato. Ma quando si arriva sulla nuova istanza della pagina, OnNavigatedTo lo riattiverebbe . Quando vuoi tornare indietro, non devi toccare NavigationCacheMode prima di chiamare Frame.GoBack . Questo dovrebbe darti l’istanza memorizzata nella cache, credo.

Credo che funzionerebbe ma non l’ho testato. Sarei curioso di sapere se lo fa. Scenario interessante lì. Mi piacerebbe vedere l’app in azione e capire meglio l’uso di questo comportamento.

Si utilizza la proprietà NavigationCacheMode per specificare se viene creata una nuova istanza della pagina per ogni visita alla pagina o se per ogni visita viene utilizzata un’istanza della pagina precedentemente creata che è stata salvata nella cache.

Il valore predefinito per la proprietà NavigationCacheMode è Disabilitato. Imposta la proprietà NavigationCacheMode su Abilitato o Richiesto quando una nuova istanza della pagina non è essenziale per ogni visita. Utilizzando un’istanza memorizzata nella cache della pagina, è ansible migliorare le prestazioni dell’applicazione e ridurre il carico sul server.

Impostare NavigationCacheMode su Required significa che la pagina viene memorizzata nella cache indipendentemente dal numero di pagine memorizzate nella cache specificate nella proprietà CacheSize. Le pagine contrassegnate come Richieste non vengono conteggiate rispetto al totale CacheSize. L’impostazione di NavigationCacheMode su Abilitato indica che la pagina viene memorizzata nella cache, ma è idonea per lo smaltimento se il numero di pagine memorizzate nella cache supera il valore di CacheSize.

Impostare la proprietà NavigationCacheMode su Disabled se deve essere creata una nuova istanza per ciascuna visita. Ad esempio, non si dovrebbe memorizzare nella cache una pagina che visualizza informazioni uniche per ogni cliente.

Il metodo OnNavigatedTo viene chiamato per ogni richiesta, anche quando la pagina viene recuperata dalla cache. Dovresti includere in questo codice di metodo che deve essere eseguito per ogni richiesta piuttosto che inserire quel codice nella funzione di costruzione della pagina.

Ho dovuto ricavare una class page2 dalla mia class di page e quindi quando voglio passare a una seconda versione della stessa pagina, page2 se l’object è page o page2 . page2 quindi a page2 se ero nella page e navigare alla page se nella page2 .

L’unico inconveniente, che è enorme, è che non c’è modo di ricavare un file XAML da un altro. Pertanto, tutto il codice C # si trova nella code page class-behind come previsto, ma ci sono due file XAML quasi identici, uno per ogni versione della pagina.

Un piccolo script potrebbe probabilmente essere aggiunto come fase di pre-build per generare la seconda class di pagina dal primo, copiando i dati XAML e regolando i nomi delle classi.

È brutto ma funziona quasi perfettamente e non devo mai preoccuparmi della duplicazione del codice C # o di problemi di cache di navigazione. Finisco per avere il codice XMAL duplicato, che nel mio caso non cambia mai davvero. Ho anche due avvertimenti per non utilizzare la new parola chiave sul codice generato automaticamente per page2.InitializeComponent() e page2.Connect() .

È interessante page2 passare alla page quindi alla page page2 quindi alla page non causa un problema e la seconda istanza della class della page è una seconda istanza effettiva non correlata alla prima.

Si noti che questa soluzione è probabilmente raccomandata contro la SM.

Ho raggiunto questo risultato:

  • Impostazione di NavigationCacheMode su Richiesto / Abilitato per le pagine richieste.
  • Facendo clic sul pulsante / collegamento Pagina2:

    Attraversa il Frame BackStack e scopri se la Pagina2 è in BackStack. Se viene trovato Pagina2, chiamare Frame.GoBack () numero di volte richiesto. Se non lo trovi, passa alla nuova pagina. Questo funzionerà per qualsiasi no di pagine.

Esempio di codice:

 public void Page2Clicked(object sender, RoutedEventArgs e) { int isPresent = 0; int frameCount = 0; //traverse BackStack in reverse order as the last element is latest page for(int index= Frame.BackStack.Count-1; index>=0;index--) { frameCount += 1; //lets say the first page name is page1 which is cached if ("Page2".Equals(Frame.BackStack[index].SourcePageType.Name)) { isPresent = 1; //Go back required no of times while (frameCount >0) { Frame.GoBack(); frameCount -= 1; } break; } } if (isPresent == 0) { Frame.Content = null; Frame.Navigate(typeof(Page2)); } } 

Questo sarà utile se i pulsanti avanti / indietro non saranno usati molto. poiché questa soluzione influenzerà la navigazione in avanti / indietro. Se si desidera utilizzare anche la navigazione avanti / indietro, è necessario gestire alcuni casi aggiuntivi.