Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
L'analisi del markup XAML per costruire oggetti in memoria richiede molto tempo per un'interfaccia utente complessa. Ecco alcune operazioni che puoi eseguire per migliorare l'analisi e il tempo di caricamento del markup XAML e l'efficienza della memoria della tua app.
All'avvio dell'app, limitare il markup XAML caricato solo a ciò che serve per l'interfaccia utente iniziale. Esaminare il markup nella pagina iniziale (incluse le risorse della pagina) e verificare che non si stia caricando elementi aggiuntivi non necessari immediatamente. Questi elementi possono provenire da un'ampia gamma di origini, ad esempio dizionari di risorse, elementi compressi e elementi sovrapposti ad altri elementi.
L'ottimizzazione del codice XAML per l'efficienza richiede compromessi; non c'è sempre una singola soluzione per ogni situazione. In questo caso, esaminiamo alcuni problemi comuni e forniamo linee guida che puoi usare per rendere i compromessi giusti per la tua app.
Riduci al minimo il numero di elementi
Anche se la piattaforma XAML è in grado di visualizzare un numero elevato di elementi, puoi rendere più veloce il layout e il rendering dell'app usando il minor numero di elementi per ottenere gli oggetti visivi desiderati.
Le scelte effettuate nel layout dei controlli dell'interfaccia utente influiscono sul numero di elementi dell'interfaccia utente creati all'avvio dell'app. Per informazioni più dettagliate sull'ottimizzazione del layout, vedi Ottimizzare il layout XAML.
Il conteggio degli elementi è estremamente importante nei modelli di dati perché ogni elemento viene creato di nuovo per ogni elemento di dati. Per informazioni sulla riduzione del numero di elementi in un elenco o in una griglia, vedi Riduzione degli elementi per elemento nell'articolo Ottimizzazione dell'interfaccia utente listView e GridView .
Ecco alcuni altri modi per ridurre il numero di elementi che l'app deve caricare all'avvio.
Rinviare la creazione dell'elemento
Se il markup XAML contiene elementi che non vengono visualizzati immediatamente, puoi rinviare il caricamento di tali elementi fino a quando non vengono visualizzati. Ad esempio, è possibile ritardare la creazione di contenuto non visibile, ad esempio una scheda secondaria in un'interfaccia utente simile a una scheda. In alternativa, è possibile visualizzare gli elementi in una visualizzazione griglia per impostazione predefinita, ma fornire all'utente un'opzione per visualizzare i dati in un elenco. È possibile ritardare il caricamento dell'elenco fino a quando non è necessario.
Usare l'attributo x:Load anziché la proprietà Visibility per controllare quando viene visualizzato un elemento. Quando la visibilità di un elemento è impostata su Collapsed, viene ignorata durante il passaggio di rendering, ma si pagano comunque i costi dell'istanza dell'oggetto in memoria. Quando si usa invece x:Load, il framework non crea l'istanza dell'oggetto finché non è necessaria, quindi i costi di memoria sono ancora inferiori. Lo svantaggio è che si paga un piccolo sovraccarico di memoria (circa 600 byte) quando l'interfaccia utente non viene caricata.
Annotazioni
È possibile ritardare il caricamento degli elementi usando l'attributo
Gli esempi seguenti mostrano la differenza nel numero di elementi e nell'uso della memoria quando vengono usate tecniche diverse per nascondere gli elementi dell'interfaccia utente. Un controllo ListView e un controllo GridView che contengono elementi identici sono inseriti nella griglia radice di una pagina. Il ListView non è visibile, ma il GridView viene visualizzato. Il codice XAML in ognuno di questi esempi produce la stessa interfaccia utente sullo schermo. Vengono usati gli strumenti di di Visual Studio per la profilatura e le prestazioni per controllare il numero di elementi e l'uso della memoria.
Opzione 1 - Inefficiente
In questo caso, il ListView viene caricato, ma non è visibile perché la sua larghezza è 0. ListView e ognuno dei relativi elementi figlio viene creato nella struttura ad albero visuale e caricato in memoria.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE.-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="List1" Width="0">
<ListViewItem>Item 1</ListViewItem>
<ListViewItem>Item 2</ListViewItem>
<ListViewItem>Item 3</ListViewItem>
<ListViewItem>Item 4</ListViewItem>
<ListViewItem>Item 5</ListViewItem>
<ListViewItem>Item 6</ListViewItem>
<ListViewItem>Item 7</ListViewItem>
<ListViewItem>Item 8</ListViewItem>
<ListViewItem>Item 9</ListViewItem>
<ListViewItem>Item 10</ListViewItem>
</ListView>
<GridView x:Name="Grid1">
<GridViewItem>Item 1</GridViewItem>
<GridViewItem>Item 2</GridViewItem>
<GridViewItem>Item 3</GridViewItem>
<GridViewItem>Item 4</GridViewItem>
<GridViewItem>Item 5</GridViewItem>
<GridViewItem>Item 6</GridViewItem>
<GridViewItem>Item 7</GridViewItem>
<GridViewItem>Item 8</GridViewItem>
<GridViewItem>Item 9</GridViewItem>
<GridViewItem>Item 10</GridViewItem>
</GridView>
</Grid>
Albero visivo dal vivo con ListView caricato. Il numero totale di elementi per la pagina è 89.
ListView e i suoi figli vengono caricati in memoria.
Opzione 2 - Migliore
In questo caso, la visibilità di ListView è impostata su compressa (l'altro codice XAML è identico all'originale). ListView viene creato nella struttura ad albero visuale, ma i relativi elementi figlio non sono ancora creati. Tuttavia, vengono caricati in memoria, quindi l'uso della memoria è identico all'esempio precedente.
<ListView x:Name="List1" Visibility="Collapsed">
Struttura ad albero visuale dinamica con ListView compresso. Il numero totale di elementi per la pagina è 46.
ListView e i suoi figli vengono caricati in memoria.
Opzione 3 - Più efficiente
In questo caso, ListView ha l'attributo x:Load impostato su False (l'altro codice XAML è identico all'originale). ListView non viene creato nell'albero visivo o caricato in memoria all'avvio.
<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">
Struttura ad albero visuale dinamica con ListView non caricata. Il numero totale di elementi per la pagina è 45.
ListView e i relativi elementi figlio non vengono caricati in memoria.
Annotazioni
I conteggi degli elementi e l'uso della memoria in questi esempi sono molto piccoli e vengono mostrati solo per illustrare il concetto. In questi esempi, il sovraccarico dell'uso di x:Load è maggiore del risparmio di memoria, quindi l'app non trarrà vantaggio. Devi usare gli strumenti di profilatura nella tua app per determinare se l'app trarrà vantaggio dal caricamento posticipato.
Usare le proprietà del pannello di layout
I pannelli di layout hanno una proprietà background, quindi non è necessario inserire un rettangolo davanti a un pannello solo per colorarlo.
inefficiente
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
<Rectangle Fill="Black"/>
</Grid>
efficiente
<Grid Background="Black"/>
I pannelli di layout hanno anche proprietà del bordo predefinite, quindi non è necessario inserire un elemento Border in un pannello di layout. Vedi Ottimizzare il layout XAML per altre info ed esempi.
Usare immagini al posto di elementi basati su vettori
Se si riutilizza lo stesso elemento basato su vettore abbastanza volte, diventa invece più efficiente usare un elemento Image . Gli elementi basati su vettori possono essere più costosi perché la CPU deve creare ogni singolo elemento separatamente. Il file di immagine deve essere decodificato una sola volta.
Ottimizzare le risorse e i dizionari di risorse
In genere si usano dizionari delle risorse per archiviare, a un livello abbastanza globale, le risorse a cui si vuole fare riferimento in più parti dell'app. Ad esempio, stili, pennelli, modelli e così via.
In generale, abbiamo ottimizzato ResourceDictionary in modo da non creare istanze delle risorse a meno che non vengano richieste. Tuttavia, è consigliabile evitare situazioni in modo che le risorse non vengano create inutilmente.
Risorse con x:Name
Usare l'attributo x:Key per fare riferimento alle risorse. Qualsiasi risorsa con l'attributo x:Name non trarrà vantaggio dall'ottimizzazione della piattaforma; invece, viene istanziata non appena viene creato il ResourceDictionary. Questo accade perché x:Name indica alla piattaforma che la tua app necessita dell'accesso diretto a questa risorsa, quindi la piattaforma deve creare qualcosa a cui fare riferimento.
ResourceDictionary in un oggetto UserControl
Un oggetto ResourceDictionary definito all'interno di un oggetto UserControl comporta una penalità. La piattaforma crea una copia di tale ResourceDictionary per ogni istanza di UserControl. Se avete un oggetto UserControl utilizzato frequentemente, spostate il ResourceDictionary dall'interno dello UserControl e posizionatelo a livello di pagina.
Ambito Resource e ResourceDictionary
Se una pagina fa riferimento a un controllo utente o a una risorsa definita in un file diverso, anche il framework analizza tale file.
In questo caso, poiché InitialPage.xaml usa una risorsa di ExampleResourceDictionary.xaml, l'intero oggetto ExampleResourceDictionary.xaml deve essere analizzato all'avvio.
InitialPage.xaml.
<Page x:Class="ExampleNamespace.InitialPage" ...>
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ExampleResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<Grid>
<TextBox Foreground="{StaticResource TextBrush}"/>
</Grid>
</Page>
ExampleResourceDictionary.xaml.
<ResourceDictionary>
<SolidColorBrush x:Key="TextBrush" Color="#FF3F42CC"/>
<!--This ResourceDictionary contains many other resources that
are used in the app, but are not needed during startup.-->
</ResourceDictionary>
Se usi una risorsa in molte pagine nell'app, archiviarla in App.xaml è una procedura consigliata ed evita la duplicazione. Ma App.xaml viene analizzato all'avvio dell'app, quindi qualsiasi risorsa utilizzata in una sola pagina (a meno che tale pagina non sia la pagina iniziale) dovrebbe essere inserita nelle risorse locali della pagina. Questo esempio mostra App.xaml contenente risorse usate da una sola pagina (non è la pagina iniziale). Questo aumenta inutilmente il tempo di avvio dell'app.
App.xaml
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Application ...>
<Application.Resources>
<SolidColorBrush x:Key="DefaultAppTextBrush" Color="#FF3F42CC"/>
<SolidColorBrush x:Key="InitialPageTextBrush" Color="#FF3F42CC"/>
<SolidColorBrush x:Key="SecondPageTextBrush" Color="#FF3F42CC"/>
<SolidColorBrush x:Key="ThirdPageTextBrush" Color="#FF3F42CC"/>
</Application.Resources>
</Application>
InitialPage.xaml.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.InitialPage" ...>
<StackPanel>
<TextBox Foreground="{StaticResource InitialPageTextBrush}"/>
</StackPanel>
</Page>
SecondPage.xaml.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.SecondPage" ...>
<StackPanel>
<Button Content="Submit" Foreground="{StaticResource SecondPageTextBrush}"/>
</StackPanel>
</Page>
Per rendere questo esempio più efficiente, sposta SecondPageTextBrush in SecondPage.xaml e ThirdPageTextBrush in ThirdPage.xaml.
InitialPageTextBrush può rimanere in App.xaml perché le risorse dell'applicazione devono essere analizzate all'avvio dell'app in ogni caso.
Consolidare più pennelli simili in un'unica risorsa.
La piattaforma XAML tenta di memorizzare nella cache gli oggetti di uso comune in modo che possano essere riutilizzati il più spesso possibile. Ma XAML non può facilmente determinare se un pennello dichiarato in un'unica parte di markup è uguale a un pennello dichiarato in un'altra. L'esempio seguente usa SolidColorBrush per illustrare, ma il caso è più probabile e più importante con GradientBrush. Controllare anche la presenza di pennelli che usano colori predefiniti; ad esempio "Orange" e "#FFFFA500" sono lo stesso colore.
inefficiente.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page ... >
<StackPanel>
<TextBlock>
<TextBlock.Foreground>
<SolidColorBrush Color="#FFFFA500"/>
</TextBlock.Foreground>
</TextBlock>
<Button Content="Submit">
<Button.Foreground>
<SolidColorBrush Color="#FFFFA500"/>
</Button.Foreground>
</Button>
</StackPanel>
</Page>
Per correggere la duplicazione, definire il pennello come risorsa. Se i controlli in altre pagine usano lo stesso pennello, spostalo in App.xaml.
efficiente.
<Page ... >
<Page.Resources>
<SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
</Page.Resources>
<StackPanel>
<TextBlock Foreground="{StaticResource BrandBrush}" />
<Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
</StackPanel>
</Page>
Ridurre al minimo l'overdrawing
L'overdrawing si verifica in cui più oggetti vengono disegnati nello stesso pixel dello schermo. Si noti che a volte c'è un compromesso tra questa guida e il desiderio di ridurre al minimo il numero di elementi.
Utilizza DebugSettings.IsOverdrawHeatMapEnabled per una diagnosi visiva. Potresti trovare oggetti disegnati che non sapevi che si trovavano nella scena.
Elementi trasparenti o nascosti
Se un elemento non è visibile perché è trasparente o nascosto dietro altri elementi e non contribuisce al layout, eliminarlo. Se l'elemento non è visibile nello stato di visualizzazione iniziale, ma è visibile in altri stati di visualizzazione, usare x:Load per controllarne lo stato o impostare Visibilitysu Collapsed sull'elemento stesso e modificare il valore su Visible negli stati appropriati. Ci saranno eccezioni a questa euristica: in generale, è preferibile impostare localmente sull'elemento il valore che una proprietà ha nella maggior parte degli stati di visualizzazione.
Elementi compositi
Usare un elemento composito invece di sovrapporre più elementi per creare un effetto. In questo esempio, il risultato è una forma a due tonalità in cui la metà superiore è nera (dallo sfondo del Grid) e la metà inferiore è grigia (dal bianco semitrasparente Rectangle alfa-miscelato sullo sfondo nero del Grid). In questo caso vengono riempiti 150% dei pixel necessari per ottenere il risultato.
inefficiente.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="1" Fill="White" Opacity=".5"/>
</Grid>
efficiente.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Black"/>
<Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>
Pannelli di disposizione
Un pannello di layout può avere due scopi: colorare un'area e disporre gli elementi figlio. Se un elemento più indietro nell'ordine z sta già colorando un'area, non è necessario che un pannello di layout di fronte dipinga quell'area: invece può solo concentrarsi sulla disposizione dei suoi figli. Ecco un esempio.
inefficiente.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
efficiente.
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Se il Grid deve essere hit testable, impostare un valore di sfondo trasparente su di esso.
Confini
Utilizzare un elemento Border per disegnare un bordo intorno a un oggetto . In questo esempio viene utilizzato un Grid come bordo provvisorio intorno a un TextBox. Ma tutti i pixel nella cella centrale vengono ritirati.
inefficiente.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Blue" Width="300" Height="45">
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition/>
<ColumnDefinition Width="5"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="1" Grid.Column="1"></TextBox>
</Grid>
efficiente.
<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
<TextBox/>
</Border>
Margini
Tenere presente i margini. Due elementi adiacenti si sovrapporranno (possibilmente accidentalmente) se delle marginazioni negative si estendono nei limiti di rendering di un elemento adiacente e causano il sovradisegno.
Memorizzare nella cache il contenuto statico
Un'altra fonte di sovradisegno è una forma costituita da molti elementi sovrapposti. Se si imposta CacheMode su BitmapCache sull' UIElement che contiene la forma composita, la piattaforma esegue il rendering dell'elemento una sola volta in una bitmap e quindi utilizza tale bitmap ad ogni fotogramma invece di ridisegnare.
inefficiente.
<Canvas Background="White">
<Ellipse Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>
L'immagine precedente è il risultato, ma ecco una mappa delle aree sovrappresse. Il rosso più scuro indica quantità più elevate di overdraw.
efficiente.
<Canvas Background="White" CacheMode="BitmapCache">
<Ellipse Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>
Si noti l'uso di CacheMode. Non utilizzare questa tecnica se una qualsiasi delle forme secondarie si anima, perché potrebbe essere necessario rigenerare la cache bitmap ad ogni fotogramma, vanificando lo scopo.
Usare XBF2
XBF2 è una rappresentazione binaria del markup XAML che evita tutti i costi di analisi del testo in fase di esecuzione. Ottimizza anche il file binario per la creazione di carichi e alberi e consente il "percorso rapido" per i tipi XAML per migliorare i costi di creazione dell'heap e degli oggetti, ad esempio VSM, ResourceDictionary, Styles e così via. È completamente mappato in memoria, quindi non esiste alcun footprint dell'heap per il caricamento e la lettura di una pagina XAML. Inoltre, riduce l'impronta su disco delle pagine XAML archiviate in un'appx. XBF2 è una rappresentazione più compatta e può ridurre il footprint del disco dei file XAML/XBF1 comparativi fino a 50%. Ad esempio, l'app Foto predefinita ha visto circa 60% riduzione dopo la conversione in XBF2 eliminando da circa 1 mb di asset XBF1 a circa 400 kb di asset XBF2. Abbiamo anche visto che le app traggono vantaggio da 15 a 20% nella CPU e da 10 a 15% nell'heap Win32.
I controlli e i dizionari predefiniti XAML forniti dal framework sono già completamente abilitati per XBF2. Per la tua app, assicurati che il file di progetto dichiari TargetPlatformVersion 8.2 o versione successiva.
Per verificare se si dispone di XBF2, aprire l'app in un editor binario; i 12 e i 13 byte sono 00 02 se si dispone di XBF2.