Caché WinRT / UWP Frame y Page: Cómo crear una nueva instancia de página en Navigate () y mantener la instancia de página en GoBack ()

Intento crear una aplicación UWP (Universal Windows App) con C #. Mi problema es el control Frame : si lo uso sin NavigationCacheMode = Required , cada vez que el usuario retrocede, la página no se guarda en la memoria y se volverá a crear. Si configuro NavigationCacheMode en Required o Enabled , volver funciona correctamente (no hay un nuevo objeto de página) pero si navego a otra página del mismo tipo, el objeto de la página anterior se recicla y se reutiliza (no hay una nueva instancia de página).

Comportamiento deseado:

¿Hay alguna manera de tener el siguiente comportamiento con el control Frame original (como en Windows Phone):

  1. Crear una nueva instancia de página en Navigate()
  2. Mantener la instancia de página en GoBack()

La única solución que conozco es crear un control Frame propio, pero esto lleva a otros problemas (por ejemplo, falta el método SetNavigationState() , etc.)

Escenario de ejemplo:

Ejemplo de aplicación simple con tres páginas: TvShowListPage , TvShowDetailsPage , SeasonDetailsPage .

  1. TvShowListPage es la página de entrada. Después de hacer clic en un TvShow navegue a TvShowDetailsPage .
  2. Ahora, en TvShowDetailsPage seleccione una temporada en la lista y vaya a TvShowDetailsPage .
  3. Si navega hacia atrás, las páginas deben permanecer en la memoria para evitar recargar las páginas.
  4. Pero si los usuarios regresan a TvShowListPage y seleccionan otra TvShow la TvShowDetailsPage se recicla y es posible que esté en el estado incorrecto (por ejemplo, mostrando el eje pivote en lugar del primero, pivote de temporadas)

Estoy buscando el comportamiento predeterminado de Windows Phone 7: Navegar crea una nueva página en la stack de páginas, al volver elimina la página superior de la stack y muestra la página anterior de la stack (almacenada en la memoria).

Solución:

Como no había solución para este problema, tuve que volver a implementar todas las clases relevantes de paginación: Página, Marco, Administrador de suspensión, etc.

La biblioteca MyToolkit que proporciona todas estas clases se puede descargar aquí: https://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview

Referencias

  • http://www.jayway.com/2012/05/25/clearing-the-windows-8-page-cache/ : No hay una buena solución
  • http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/88e6d1b3-1fa6-4ab4-a816-e77c86ef236f/ : La implementación de una clase propia de Frame no es una solución ya que no funciona con SuspensionManager

Como no había solución para este problema, tuve que volver a implementar todas las clases relevantes de paginación: Página, Marco, Administrador de suspensión, etc.

La solución se puede descargar aquí: https://github.com/MyToolkit/MyToolkit/wiki/Paging-Overview

Actualizar:

La clase de página ahora también proporciona el método OnNavigatingFromAsync para mostrar, por ejemplo, una ventana emergente asíncrona y cancelar la navegación si es necesario …

Tuve el mismo problema. Lo quería de tal manera que cuando me estaba moviendo hacia adelante en Metro (Windows Store sería apropiado), crearía una nueva instancia. Sin embargo, cuando regrese, mantendría los datos que quería guardar.

Por lo tanto, también utilicé NavigationCacheMode = NavigationCacheMode.Enabled. Descubrí que, independientemente de la ruta por la que atravesaba, hacia adelante o hacia atrás, siempre se guardaba todo. Por lo tanto, avanzaría varias páginas, luego daré un paso atrás. Esperando que todo se restableciera mientras avanzaba, invariablemente descubrí que no lo era; había conservado los datos.

Intenté todo, incluso escribir mi propio código de botón para incluir NavigationCacheMode = NavigationCacheMode.Disabled, pero fue en vano. Como otros han señalado, una vez que lo haya habilitado, NavigationCacheMode simplemente no se desactivará.

Encontré una solución. Fui a LayoutAwarePage.cs y simplemente hice un cambio menor. Debajo de “OnNavigatedTo” encontré la línea:

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

Sin embargo, el comentario fue contrario a lo que yo quería. Estaba buscando carga de estado en un patrón unidireccional. Si avanzaba, quería carga de estado; si retrocediera, quería el comportamiento indicado por el comentario, sin carga de estado.

Así que simplemente modifiqué la línea.

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

Lo he probado y funciona perfectamente. Ahora, al navegar hacia atrás, recuerda el estado y mantiene la página igual. Navegando hacia adelante, carga fresco.

Quizás no sea la mejor práctica, pero no llamo “OnNavigatedTo” desde mi código subyacente. Hago todo a través de “LoadState”. Si está anulando “OnNavigatedTo” en el código subyacente, es posible que vea un comportamiento diferente.

Gracias,

Joseph Irvine

Cuando está navegando hacia adelante , ¿puede configurar NavigationCacheMode en Disabled antes de llamar a Frame.Navigate ? Luego, en OnNavigatedTo () establece NavigationCacheMode de nuevo en Habilitado .

Eso debería hacer que cuando vaya hacia adelante, el almacenamiento en caché esté deshabilitado. Pero cuando llegue a la nueva instancia de página, OnNavigatedTo lo habilitará de nuevo. Cuando desee navegar hacia atrás, no tocará NavigationCacheMode antes de llamar a Frame.GoBack . Eso debería darte la instancia en caché, creo.

Creo que esto funcionaría pero no lo he probado. Me gustaría saber si lo hace. Escenario interesante allí. Me encantaría ver la aplicación en acción y comprender mejor el uso de este comportamiento.

Utiliza la propiedad NavigationCacheMode para especificar si se crea una nueva instancia de la página para cada visita a la página o si se utiliza una instancia previamente construida de la página que se ha guardado en la memoria caché para cada visita.

El valor predeterminado para la propiedad NavigationCacheMode es Disabled. Establezca la propiedad NavigationCacheMode en Enabled or Required cuando una nueva instancia de la página no sea esencial para cada visita. Al usar una instancia en caché de la página, puede mejorar el rendimiento de su aplicación y reducir la carga en su servidor.

Si se establece NavigationCacheMode como requerido, la página se almacenará en caché independientemente del número de páginas almacenadas en caché especificadas en la propiedad CacheSize. Las páginas marcadas como Requerido no cuentan contra el total de CacheSize. Configurar NavigationCacheMode en Enabled significa que la página está en caché, pero es elegible para su eliminación si el número de páginas en caché excede el valor de CacheSize.

Establezca la propiedad NavigationCacheMode en Disabled si se debe crear una nueva instancia para cada visita. Por ejemplo, no debe almacenar en caché una página que muestre información que sea exclusiva de cada cliente.

Se llama al método OnNavigatedTo para cada solicitud, incluso cuando la página se recupera de la memoria caché. Debe incluir en este código de método que se debe ejecutar para cada solicitud en lugar de colocar ese código en el constructor de página.

Tuve que derivar una clase de page2 de mi clase de page y luego, cuando quiero navegar a una segunda versión de la misma página, detecto si el objeto es page o page2 . Luego, page2 a la page2 si estaba en la page y page2 a la page si en la page page2 .

El único inconveniente, que es enorme, es que no hay forma de derivar un archivo XAML de otro. Por lo tanto, todo el código C # está en el código de clase de la page detrás de lo esperado, pero hay dos archivos XAML casi idénticos, uno para cada versión de la página.

Un pequeño script probablemente podría agregarse como un paso previo a la construcción para generar la segunda clase de página desde la primera, copiando los datos XAML y ajustando los nombres de las clases.

Es feo pero casi funciona a la perfección y nunca tengo que preocuparme por la duplicación del código C o por los extraños problemas del caché de navegación. Acabo de tener un código XMAL duplicado, que en mi caso realmente nunca cambia de ninguna manera. También termino con dos advertencias sobre no usar la new palabra clave en el código generado automáticamente para page2.InitializeComponent() y page2.Connect() .

Curiosamente, navegar a la page y luego a la page page2 luego a la page no causa un problema y la segunda instancia de la clase de la page es una segunda instancia real no relacionada con la primera.

Tenga en cuenta que esta solución probablemente sea recomendada por MS.

Logré esto por:

  • Configurando NavigationCacheMode en Required / Enabled para las páginas requeridas.
  • Al hacer clic en el botón / enlace de Página2:

    Atraviese BackStack de Frame y descubra si Page2 está en BackStack. Si se encuentra Page2, llame al Frame.GoBack () requerido número de veces. Si no se encuentra solo navegue a la nueva página. Esto funcionará para cualquier número de páginas.

Muestra de código:

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

Esto será útil si no se usan mucho los botones de avance / retroceso. ya que esta solución afectará la navegación hacia adelante / hacia atrás. Si desea utilizar la navegación hacia adelante / hacia atrás también, se deben manejar algunos casos adicionales.