Procedure consigliate per le prestazioni di avvio dell'app

Creare app UWP (Universal Windows Platform) con tempi di avvio ottimali migliorando il modo in cui gestisci avvio e attivazione.

Procedure consigliate per le prestazioni di avvio dell'app

La percezione degli utenti relativamente alla velocità o alla lentezza delle app dipende in parte da quanto tempo impiega un'app per l'avvio. Ai fini di questo argomento, il tempo di avvio di un'app inizia quando l'utente avvia l'app e termina quando l'utente può interagire con l'app. Questa sezione contiene alcuni suggerimenti su come migliorare le prestazioni di avvio dell'app.

Misurazione del tempo di avvio dell'app

Assicurarsi di avviare l'app alcune volte prima di misurarne effettivamente il tempo di avvio. Ciò offre una linea di base per la misurazione e garantisce di misurare il tempo di avvio più breve possibile.

Quando viene installata nei computer dei clienti, l'app UWP è stata compilata con il toolchain .NET Native. .NET Native è una tecnologia di precompilazione che converte MSIL in codice macchina eseguibile in modalità nativa. Le app .NET Native vengono avviate più velocemente, usano meno memoria e meno batteria rispetto alle controparti MSIL. Le applicazioni create con .NET Native sono collegate a livello statico a un runtime personalizzato e al nuovo framework convergente .NET Core che può essere eseguito su tutti i dispositivi, quindi non dipendono dall'implementazione di .NET inclusa. In un computer di sviluppo, per impostazione predefinita l'app usa .NET Native se si sta sviluppando in modalità "Release" e usa invece CoreCLR se si sta sviluppando in modalità "Debug". È possibile definire questa configurazione in Visual Studio dalla pagina Genera - "Proprietà" (C#) oppure Compilare-> Avanzate in "Progetto" (VB). Cercare la casella di controllo "Compilare con la toolchain .NET Native".

Si dovranno ottenere misurazioni che siano rappresentative dell'esperienza dell'utente finale. Di conseguenza, per verificare se si sta compilando l'app in codice nativo nel computer di sviluppo, è possibile eseguire lo strumento Native Image Generator (Ngen.exe) per precompilare l'app prima di misurarne il tempo di avvio.

La procedura seguente descrive come eseguire Ngen.exe per precompilare l'app.

Per eseguire Ngen.exe

  1. Eseguire l'app almeno una volta per assicurarsi che Ngen.exe la rilevi.

  2. Aprire l’Utilità di pianificazione in uno dei modi indicati di seguito:

    • Cercare "Utilità di pianificazione" dalla schermata Start.
    • Eseguire "taskschd.msc".
  3. Nel riquadro di sinistra dell’Utilità di pianificazione, espandere Libreria Utilità di pianificazione.

  4. Espandere Microsoft.

  5. Espandere Windows.

  6. Selezionare .NET Framework.

  7. Selezionare .NET Framework NGEN 4.x dall’elenco attività.

    Se si usa un computer a 64 bit, è disponibile anche .NET Framework NGEN v4.x 64. Se si sta compilando un’app a 64 bit, selezionare .NET Framework NGEN v4.x 64.

  8. Scegliere Eseguire dal menu Azione.

Ngen.exe precompila tutte le app del computer che sono state utilizzate e che non dispongono di immagini native. Se le app da precompilare sono molte, l'operazione può richiedere molto tempo, ma le successive esecuzioni saranno molto più rapide.

Quando si ricompila l'app, l'immagine nativa non viene più utilizzata. Invece, l'app viene compilata just-in-time, ovvero durante l'esecuzione. Per ottenere una nuova immagine nativa, sarà necessario eseguire nuovamente Ngen.exe.

Rinviare il lavoro, se possibile

Per migliorare il tempo di avvio dell'app, esegui solo il lavoro assolutamente necessario per consentire all'utente di interagire con l'app. Il rinvio del caricamento di assembly aggiuntivi può essere particolarmente vantaggioso. Common language runtime carica un assembly al primo utilizzo. Se si riesce a ridurre al minimo il numero di assembly caricati, si potrà migliorare il tempo di avvio dell'app e il consumo di memoria.

Eseguire il lavoro che richiede tempo in modo indipendente

L’app può essere interattiva persino in presenza di parti non completamente funzionanti. Ad esempio, se nell'app vengono visualizzati dati che richiedono tempo per il recupero, si può far sì che quel codice si esegua in modo indipendente dal codice di avvio dell'app recuperando i dati in modo asincrono. Quando i dati sono disponibili, popolare l'interfaccia utente dell'app con i dati.

Poiché molte delle API UWP (Universal Windows Platform) che recuperano dati sono asincrone, è probabile che il recupero dei dati avvenga comunque in modo asincrono. Per altre info sulle API asincrone, vedere Chiamare API asincrone in C# o Visual Basic. Se il lavoro non usa API asincrone, è possibile usare la classe Task per le operazioni prolungate in modo da non bloccare l'utente nell'interazione con l'app. In tal modo l'app continuerà a rispondere all'utente mentre i dati vengono caricati.

Se l'app richiede tempi particolarmente lunghi per caricare parte dell'interfaccia utente, si può prendere in considerazione l'aggiunta di una stringa che indichi l'acquisizione in corso dei dati più recenti, in modo che gli utenti sappiano che l'app è in funzione.

Ridurre al minimo il tempo di avvio

Ad eccezione delle app più semplici, tutte le app richiedono una discreta quantità di tempo per caricare le risorse, analizzare il codice XAML, configurare le strutture di dati ed eseguire la logica all'attivazione. In questo argomento analizzeremo il processo di attivazione suddividendolo in tre fasi. Forniremo anche alcuni suggerimenti per ridurre il tempo dedicato a ogni fase e alcune tecniche per rendere più piacevole per l'utente ogni fase dell'avvio dell'app.

Il periodo di attivazione è il tempo che intercorre da quando un utente avvia un'app a quando l'app acquista tutte le sue funzionalità. Si tratta di un periodo particolarmente importante, perché è lì che gli utenti si formano la prima impressione sull'app. Si aspettano un feedback immediato e continuo dal sistema e dalle app. Se un'app non viene avviata rapidamente, l'utente percepisce il sistema e l'app come non funzionanti o mal progettati. Peggio ancora, se un'app impiega troppo tempo ad attivarsi, Gestione del ciclo di vita dei processi potrebbe terminarla oppure l'utente potrebbe disinstallarla.

Introduzione delle fasi di avvio

L'avvio prevede un determinato numero di elementi mobili che devono essere coordinati in modo corretto per garantire un'esperienza utente ottimale. I passaggi seguenti vengono eseguiti nell'intervallo che intercorre tra il momento in cui l'utente fa clic sul riquadro dell'app e la visualizzazione del contenuto dell'applicazione.

  • La shell di Windows avvia il processo e viene chiamato Main.
  • Viene creato l'oggetto Application.
    • (Modello di progetto) Il costruttore chiama InitializeComponent. Questa operazione comporta l'analisi di App.xaml e la creazione degli oggetti.
  • Viene generato l'evento Application.OnLaunched.
    • (Modello di progetto) Il codice dell'app crea un frame e passa a MainPage.
    • (Modello di progetto) Il costruttore MainPage chiama InitializeComponent. Questa operazione comporta l'analisi di MainPage.xaml e la creazione degli oggetti.
    • (Modello di progetto) Viene chiamato Window.Current.Activate().
  • La piattaforma XAML esegue il passaggio di layout, inclusi Measure & Arrange.
    • ApplyTemplate determinerà la creazione del contenuto del modello di controllo per ciascun controllo, che in genere rappresenta la maggior parte del tempo di avvio del layout.
  • Il rendering viene chiamato per creare elementi visivi per tutti i contenuti della finestra.
  • Il frame viene visualizzato in Gestione finestre desktop.

Semplificazione del percorso di avvio

Escludere dal codice di avvio qualsiasi elemento non necessario per il primo frame.

  • Se sono presenti DLL utente contenenti controlli non necessari durante il primo frame, valutare la possibilità di ritardarne il caricamento.
  • Se una parte dell'interfaccia utente dipende dai dati nel cloud, suddividere l'interfaccia utente. Visualizzare innanzitutto l'interfaccia utente indipendente dai dati del cloud e quindi visualizzare in modo asincrono l'interfaccia utente dipendente dal cloud. Considerare anche di usare la memorizzazione nella cache dei dati in locale in modo che l'applicazione possa funzionare non in linea e non venga influenzata dalla lentezza della connettività di rete.
  • Mostrare lo stato dell'interfaccia utente se l'interfaccia utente è in attesa di dati.
  • Prestare attenzione se la progettazione dell'app prevede numerose fasi di analisi dei file di configurazione oppure un'interfaccia utente generata dinamicamente dal codice.

Ridurre il numero di elementi

Le prestazioni di avvio in un'app XAML sono direttamente correlate al numero di elementi creati durante l'avvio. Meno elementi si creano, meno tempo richiederà l'avvio dell'app. Come riferimento approssimativo, considerare che la creazione di ogni elemento richiede 1 millisecondo.

  • I modelli usati nei controlli elemento possono avere l'impatto maggiore perché si ripetono più volte. Vedere Ottimizzazione dell’interfaccia utente in ListView e GridView.
  • I modelli di controlli utente e i controlli verranno espansi per poter essere presi in considerazione.
  • Se si crea codice XAML che non viene visualizzato sullo schermo, si deve giustificare se queste parti del codice XAML devono essere create durante l'avvio.

Nella finestra Albero elementi visivi attivi Visual Studio è visualizzato il numero di elementi figlio per ogni nodo dell’albero.

Struttura ad albero visuale attiva.

Usa il rinvio. La compressione di un elemento o l'impostazione della relativa opacità su 0 non impedirà la creazione dell'elemento. Con x:Load o x:DeferLoadStrategy puoi ritardare il caricamento di un elemento dell'interfaccia utente e caricarlo quando necessario. È consigliabile ritardare l'elaborazione dell'interfaccia utente non visibile nella schermata di avvio, in modo da poterla caricare quando necessario oppure come parte di un insieme di logica posticipata. Per attivare il caricamento, si deve chiamare solo FindName per l'elemento. Per un esempio e altre informazioni, vedi Attributo x:Load e Attributo x:DeferLoadStrategy.

Virtualizzazione. Se l'interfaccia utente include contenuto ripetuto o elenco, è consigliabile usare la virtualizzazione dell'interfaccia utente. Se l'interfaccia utente per gli elenchi non viene virtualizzata, tutti gli elementi verranno creati a monte e questa situazione può rallentare l'avvio. Vedere Ottimizzazione dell’interfaccia utente in ListView e GridView.

Le prestazioni dell'applicazione non fanno solo riferimento alle prestazioni vere e proprie, ma anche alla percezione. La modifica dell'ordine delle operazioni in modo che gli elementi visivi vengano eseguiti per primi fa sì che l'utente abbia l'impressione che l'applicazione sia più veloce. Gli utenti ritengono che l'applicazione sia caricata quando il contenuto è visualizzato sullo schermo. In genere, le applicazioni devono eseguire più operazioni durante l'avvio e non tutte queste operazioni sono necessarie per visualizzare l'interfaccia utente. Pertanto, le operazioni non necessarie dovrebbero essere posticipate o classificate con una priorità inferiore rispetto all'interfaccia utente.

In questo argomento si fa riferimento al "primo frame", termine che proviene dal mondo dell'animazione/TV ed è la misura dell'intervallo di tempo necessario all'utente finale per vedere il contenuto.

Migliorare la percezione all'avvio

Si userà qui l'esempio di un semplice gioco online per identificare ogni fase dell'avvio e diverse tecniche per fornire feedback all'utente durante il processo. Per questo esempio, la prima fase dell'attivazione è il tempo che intercorre tra il momento in cui l'utente tocca il riquadro del gioco e l'inizio dell'esecuzione del codice del gioco. Durante questo intervallo di tempo il sistema non ha alcun contenuto da visualizzare all'utente per indicare che il gioco corretto è stato avviato. Questo contenuto può essere fornito con una schermata iniziale. Il gioco informa quindi l'utente che la prima fase dell'attivazione è stata completata sostituendo la schermata iniziale statica con la propria interfaccia utente quando inizia a eseguire il codice.

La seconda fase dell'attivazione comprende la creazione e l'inizializzazione di strutture di importanza critica per il gioco. Se un'app è in grado di creare rapidamente l'interfaccia utente iniziale con i dati disponibili dopo la prima fase dell'attivazione, la seconda fase è trascurabile e l'interfaccia può essere visualizzata immediatamente. In caso contrario, è opportuno che l'app visualizzi una pagina di caricamento mentre viene inizializzata.

Si può scegliere l'aspetto della pagina di caricamento; può visualizzare semplicemente una barra o un anello di stato. L'importante è che l'app indichi che sta eseguendo delle attività prima di rispondere alle azioni dell'utente. Nel caso del gioco, per visualizzare l'interfaccia della schermata iniziale è necessario caricare alcune immagini e suoni dal disco in memoria. Queste attività impiegano un paio di secondi, quindi l'app informa l'utente sul fatto di non essersi bloccata sostituendo la schermata iniziale con una pagina di caricamento, che mostra una semplice animazione correlata al tema del gioco.

La terza fase inizia dopo che il gioco ha raccolto un minimo di informazioni per creare un'interfaccia utente interattiva, che sostituisce la pagina di caricamento. A questo punto le uniche informazioni disponibili per il gioco online sono costituite dal contenuto che l'app ha caricato dal disco. Il gioco può essere corredato di contenuto sufficiente per creare un'interfaccia utente interattiva, ma, essendo un gioco online, non funzionerà finché non si connette a Internet e scarica alcune info aggiuntive. Nel frattempo, l'utente può interagire con l'interfaccia utente, ma le funzionalità che hanno bisogno di ulteriori dati dal Web dovrebbero indicare in qualche modo che il contenuto è ancora in fase di caricamento. Un'app potrebbe impiegare un certo tempo prima di diventare completamente funzionante, quindi è importante che le funzionalità vengano rese disponibili nel più breve tempo possibile.

Ora che sono state identificate le tre fasi dell'attivazione nel gioco online, è possibile collegarle al codice effettivo.

Fase 1

Prima di avviarsi, un'app deve indicare al sistema cosa deve essere visualizzato come schermata iniziale. A questo scopo l'app fornisce un'immagine e un colore di sfondo all'elemento SplashScreen in un manifesto dell'app, come nell'esempio. Windows visualizza tutto questo quando inizia l'attivazione dell'app.

<Package ...>
  ...
  <Applications>
    <Application ...>
      <VisualElements ...>
        ...
        <SplashScreen Image="Images\splashscreen.png" BackgroundColor="#000000" />
        ...
      </VisualElements>
    </Application>
  </Applications>
</Package>

Per altre info, vedere Aggiunta di una schermata iniziale.

Usare il costruttore dell'app solo per inizializzare le strutture di dati importanti per l'app. Il costruttore viene chiamato solo alla prima esecuzione dell'app e non necessariamente ogni volta che l'app viene attivata. Ad esempio, il costruttore non viene chiamato per un'app che viene eseguita, messa in background e quindi attivata tramite il contratto Ricerca.

Fase 2

Esistono diversi buoni motivi per attivare un'app, ognuno dei quali può essere gestito in modo diverso. È possibile eseguire l’override dei metodi OnActivated, OnCachedFileUpdaterActivated, OnFileActivated, OnFileOpenPickerActivated, OnFileSavePickerActivated, OnLaunched, OnSearchActivated e OnShareTargetActivated per gestire ogni motivo. Una delle azioni che un’app deve eseguire in questi metodi è creare un’interfaccia utente, assegnarla a Window.Content e quindi chiamare Window.Activate. A questo punto la schermata iniziale viene sostituita dall'interfaccia utente creata dall'app. Questa interfaccia può essere la schermata di caricamento oppure l'effettiva interfaccia utente dell'app, se al momento dell'attivazione sono disponibili informazioni sufficienti per crearla.

public partial class App : Application
{
    // A handler for regular activation.
    async protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        base.OnLaunched(args);

        // Asynchronously restore state based on generic launch.

        // Create the ExtendedSplash screen which serves as a loading page while the
        // reader downloads the section information.
        ExtendedSplash eSplash = new ExtendedSplash();

        // Set the content of the window to the extended splash screen.
        Window.Current.Content = eSplash;

        // Notify the Window that the process of activation is completed
        Window.Current.Activate();
    }

    // a different handler for activation via the search contract
    async protected override void OnSearchActivated(SearchActivatedEventArgs args)
    {
        base.OnSearchActivated(args);

        // Do an asynchronous restore based on Search activation

        // the rest of the code is the same as the OnLaunched method
    }
}

partial class ExtendedSplash : Page
{
    // This is the UIELement that's the game's home page.
    private GameHomePage homePage;

    public ExtendedSplash()
    {
        InitializeComponent();
        homePage = new GameHomePage();
    }

    // Shown for demonstration purposes only.
    // This is typically autogenerated by Visual Studio.
    private void InitializeComponent()
    {
    }
}
    Partial Public Class App
    Inherits Application

    ' A handler for regular activation.
    Protected Overrides Async Sub OnLaunched(ByVal args As LaunchActivatedEventArgs)
        MyBase.OnLaunched(args)

        ' Asynchronously restore state based on generic launch.

        ' Create the ExtendedSplash screen which serves as a loading page while the
        ' reader downloads the section information.
        Dim eSplash As New ExtendedSplash()

        ' Set the content of the window to the extended splash screen.
        Window.Current.Content = eSplash

        ' Notify the Window that the process of activation is completed
        Window.Current.Activate()
    End Sub

    ' a different handler for activation via the search contract
    Protected Overrides Async Sub OnSearchActivated(ByVal args As SearchActivatedEventArgs)
        MyBase.OnSearchActivated(args)

        ' Do an asynchronous restore based on Search activation

        ' the rest of the code is the same as the OnLaunched method
    End Sub
End Class

Partial Friend Class ExtendedSplash
    Inherits Page

    Public Sub New()
        InitializeComponent()

        ' Downloading the data necessary for
        ' initial UI on a background thread.
        Task.Run(Sub() DownloadData())
    End Sub

    Private Sub DownloadData()
        ' Download data to populate the initial UI.

        ' Create the first page.
        Dim firstPage As New MainPage()

        ' Add the data just downloaded to the first page

        ' Replace the loading page, which is currently
        ' set as the window's content, with the initial UI for the app
        Window.Current.Content = firstPage
    End Sub

    ' Shown for demonstration purposes only.
    ' This is typically autogenerated by Visual Studio.
    Private Sub InitializeComponent()
    End Sub
End Class

Le app che visualizzano una pagina di caricamento nel gestore dell'attivazione iniziano a creare l'interfaccia utente in background. Dopo che l’elemento è stato creato, viene generato il relativo evento FrameworkElement.Loaded. Nel gestore dell'evento si deve quindi sostituire il contenuto della finestra, che attualmente corrisponde alla schermata di caricamento, con la home page appena creata.

È fondamentale che un'app con un periodo di inizializzazione prolungato visualizzi una pagina di caricamento. A prescindere dal fatto di fornire indicazioni all'utente sul processo di attivazione, il processo verrà terminato se Window.Activate non viene chiamato entro 15 secondi dall'avvio del processo di attivazione.

partial class GameHomePage : Page
{
    public GameHomePage()
    {
        InitializeComponent();

        // add a handler to be called when the home page has been loaded
        this.Loaded += ReaderHomePageLoaded;

        // load the minimal amount of image and sound data from disk necessary to create the home page.
    }

    void ReaderHomePageLoaded(object sender, RoutedEventArgs e)
    {
        // set the content of the window to the home page now that it's ready to be displayed.
        Window.Current.Content = this;
    }

    // Shown for demonstration purposes only.
    // This is typically autogenerated by Visual Studio.
    private void InitializeComponent()
    {
    }
}
    Partial Friend Class GameHomePage
    Inherits Page

    Public Sub New()
        InitializeComponent()

        ' add a handler to be called when the home page has been loaded
        AddHandler Me.Loaded, AddressOf ReaderHomePageLoaded

        ' load the minimal amount of image and sound data from disk necessary to create the home page.
    End Sub

    Private Sub ReaderHomePageLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' set the content of the window to the home page now that it's ready to be displayed.
        Window.Current.Content = Me
    End Sub

    ' Shown for demonstration purposes only.
    ' This is typically autogenerated by Visual Studio.
    Private Sub InitializeComponent()
    End Sub
End Class

Per un esempio di uso di schermate iniziali estese, vedere Esempio di schermata iniziale.

Fase 3

Il fatto che l'app visualizzi l'interfaccia utente non significa che sia completamente pronta per l'uso. Nel caso del nostro gioco, l'interfaccia utente viene visualizzata con dei segnaposti per le funzionalità che necessitano di dati da Internet. A questo punto il gioco scarica i dati aggiuntivi necessari per rendere l'app completamente funzionante e abilita progressivamente le funzionalità man mano che i dati vengono acquisiti.

In alcuni casi buona parte del contenuto necessario per l'attivazione può essere incluso nel pacchetto dell'app. È il caso di un gioco semplice. In questi casi il processo di attivazione è piuttosto semplice. Tuttavia molti programmi, come i lettori di news e i visualizzatori di foto, devono recuperare informazioni dal Web per poter funzionare. Questi dati possono essere di grandi dimensioni e il download può richiedere parecchio tempo. Il modo in cui l'app recupera questi dati durante il processo di attivazione può avere un enorme impatto sulle prestazioni percepite dell'app.

La pagina di caricamento o, peggio ancora, la schermata iniziale potrebbe rimanere visualizzata per dei minuti se un'app cercasse di scaricare un intero set di dati necessari per il funzionamento nella prima o nella seconda fase dell'attivazione. In questo modo l'app apparirebbe bloccata o potrebbe essere terminata dal sistema. È preferibile che un'app scarichi una quantità minima di dati per mostrare un'interfaccia utente interattiva con elementi segnaposto nella fase 2 e quindi carichi progressivamente i dati, sostituendo i segnaposti, nella fase 3. Per altre informazioni sulla gestione dei dati, vedere Ottimizzare ListView e GridView.

È possibile scegliere il modo esatto in cui un'app reagisce a ogni fase del processo di avvio, ma fornendo all'utente il maggior numero di indicazioni possibile (schermata iniziale, schermata di caricamento, interfaccia utente durante il caricamento dei dati) si può trasmettere un senso di rapidità dell'app e anche del sistema nel suo complesso.

Ridurre al minimo gli assembly gestiti nel percorso di avvio

Il codice riutilizzabile ha spesso la forma di moduli (DLL) inclusi in un progetto. Per caricare questi moduli è necessario accedere al disco e, come si può immaginare, i costi di questa operazione si sommano. L'impatto maggiore si verifica sull'avvio a freddo, ma anche l'avvio a caldo può esserne influenzato. Nel caso di C# e Visual Basic, CLR tenta di ritardare il più possibile questo costo caricando gli assembly su richiesta. Questo significa che CLR non carica un modulo finché un metodo eseguito non vi fa riferimento. Consigliamo quindi di fare riferimento solo agli assembly necessari per avviare la propria app nel codice di avvio in modo che CLR non carichi i moduli non necessari. Se il percorso di avvio contiene percorsi di codice inutilizzati con riferimenti non necessari, spostare questi percorsi di codice ad altri metodi per evitare caricamenti non necessari.

Un altro modo per ridurre i caricamenti di moduli consiste nel combinare i moduli dell'app. Il caricamento di un unico assembly di grandi dimensioni in genere richiede meno tempo del caricamento di due assembly di dimensioni inferiori. Questo non è sempre possibile e consigliamo di combinare moduli solo se questa operazione non influisce sulla produttività degli sviluppatori e sulla riusabilità del codice. Si possono usare strumenti come PerfView o Analizzatore prestazioni Windows per individuare i moduli che vengono caricati all’avvio.

Inviare richieste Web intelligenti

È possibile ridurre drasticamente i tempi di caricamento di un app includendo nel pacchetto locale i contenuti, ad esempio XAML, immagini e altri file importanti per l'app. Le operazioni su disco sono infatti più veloci delle operazioni in rete. Se un'app ha bisogno di un determinato file durante l'inizializzazione, si possono ridurre i tempi di avvio globali caricandolo dal disco invece di recuperarlo da un server remoto.

Registrazione e memorizzazione nella cache delle pagine in modo efficiente

Il controllo Frame offre funzionalità di spostamento. Fornisce spostamento verso una pagina (metodo Navigate), registrazione degli spostamenti (proprietà BackStack/ForwardStack, metodo GoForward/GoBack), memorizzazione delle pagine nella cache (Page.NavigationCacheMode) e supporto della serializzazione (metodo GetNavigationState).

Le prestazioni da considerare con il controllo Frame fanno principalmente riferimento alla registrazione e alla memorizzazione delle pagine nella cache.

Registrazione dei frame. Quando ci si sposta su una pagina mediante Frame.Navigate(), alla raccolta Frame.BackStack viene aggiunta PageStackEntry per la pagina corrente. PageStackEntry ha dimensioni relativamente ridotte. Pertanto non c'è alcun limite predefinito per le dimensioni della raccolta BackStack. Potenzialmente un utente potrebbe eseguire spostamenti in ciclo e pertanto accrescere all'infinito la raccolta.

PageStackEntry include anche il parametro passato al metodo Frame.Navigate(). È consigliabile che il parametro sia di tipo serializzabile primitivo (ad esempio intero o stringa) per garantire il corretto funzionamento del metodo Frame.GetNavigationState(). Tuttavia, tale parametro potrebbe potenzialmente fare riferimento a un oggetto che richiede quantità più significative di working set o altre risorse, rendendo molto più dispendiosa ogni immissione nel backstack. Ad esempio, si può potenzialmente usare StorageFile come parametro. Di conseguenza BackStack manterrà aperto un numero indefinito di file.

Pertanto è consigliabile ridurre al minimo i parametri di spostamento e limitare le dimensioni di BackStack. BackStack è un vettore standard (IList in C#, Platform::Vector in C++/CX) e quindi può essere ridotto mediante la rimozione delle voci.

Memorizzazione delle pagine nella cache. Per impostazione predefinita, quando ci si sposta su una pagina con il metodo Frame.Navigate, viene creata una nuova istanza della pagina. Allo stesso modo, tonando alla pagina precedente con Frame.GoBack, viene allocata una nuova istanza della pagina precedente.

Tuttavia, i frame offrono una cache di pagina facoltativa che consente di evitare questa creazione di istanze. Per memorizzare una pagina nella cache, usare la proprietà Page.NavigationCacheMode. L'impostazione di questa modalità su Required comporta la memorizzazione della pagina nella cache. Se invece si imposta su Enabled, la memorizzazione della pagina nella cache viene consentita. Per impostazione predefinita le dimensioni della cache sono pari a 10 pagine, ma è possibile eseguire l'override di questa operazione mediante la proprietà Frame.CacheSize. Tutte le pagine con impostazione Required verranno memorizzate nella cache e se è presente un numero di pagine minore rispetto al valore Required di CacheSize, verranno memorizzate nella cache anche le pagine con impostazione Enabled.

La memorizzazione delle pagine nella cache può migliorare le prestazioni evitando la creazione di istanze e pertanto ottimizzando le prestazioni di spostamento, ma può anche compromettere le prestazioni a causa di un'eccessiva memorizzazione nella cache e del conseguente impatto negativo sul working set.

Pertanto è consigliabile usare la memorizzazione delle pagine nella cache in modo appropriato per l'applicazione specifica, Ad esempio, un'app visualizza un elenco di elementi in un frame e, quando si tocca un elemento, si sposta dal frame a una pagina dei dettagli relativi a tale elemento. La pagina dell'elenco probabilmente deve essere impostata per essere memorizzata nella cache. Se la pagina dei dettagli è identica per tutti gli elementi, è consigliabile memorizzare nella cache anche tale pagina. Tuttavia, se la pagina dei dettagli è più eterogenea, potrebbe essere consigliabile non attivare la memorizzazione nella cache.