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 delle app è uno strumento che fornisce agli sviluppatori una notifica di problemi di prestazioni. L'analisi delle app verifica il codice della tua app rispetto a un insieme di linee guida sulle prestazioni e sulle migliori pratiche.
L'analisi delle app identifica i problemi utilizzando un insieme di regole comuni per le problematiche di prestazioni che le app possono incontrare. Quando appropriato, l'analisi dell'app punterà allo strumento di sequenza temporale, alle informazioni sull'origine e alla documentazione di Visual Studio per consentirti di investigare.
Le regole in Analisi app fanno riferimento a una linea guida o a una procedura consigliata rispetto alla quale viene verificata l'app.
Dimensioni dell'immagine decodificate maggiori delle dimensioni del rendering
Le immagini vengono acquisite a risoluzioni molto elevate, che possono portare ad app che usano più CPU durante la decodifica dei dati dell'immagine e una maggiore memoria dopo il caricamento dal disco. Ma non c'è senso decodificare e salvare un'immagine ad alta risoluzione in memoria solo per visualizzarla più piccola delle dimensioni native. Creare invece una versione dell'immagine con le dimensioni esatte che verrà disegnata sullo schermo usando le proprietà DecodePixelWidth e DecodePixelHeight.
Impatto
La visualizzazione di immagini con dimensioni non native può influire negativamente sia sul tempo della CPU (a causa della decodifica delle dimensioni appropriate e del tempo di download) che della memoria.
Cause e soluzioni
L'immagine non viene impostata in modo asincrono
L'app usa SetSource() anziché SetSourceAsync(). È consigliabile evitare sempre di usare SetSource e usare setSourceAsync quando si imposta un flusso per decodificare le immagini in modo asincrono.
L'immagine viene chiamata quando ImageSource non si trova nell'albero attivo
BitmapImage è connesso all'albero XAML attivo dopo aver impostato il contenuto con SetSourceAsync o UriSource. Devi sempre associare un BitmapImage all'albero attivo prima di impostare la sorgente. Ogni volta che un elemento dell'immagine o un pennello viene specificato nel markup, ciò avverrà automaticamente. Di seguito sono riportati esempi.
esempi di alberi vivi
Esempio 1 (valido): URI (Uniform Resource Identifier) specificato nel markup.
<Image x:Name="myImage" UriSource="Assets/cool-image.png"/>
Markup di esempio 2: URI specificato nel codice sottostante.
<Image x:Name="myImage"/>
Esempio 2 code-behind (valido): connessione di BitmapImage all'albero prima di impostare UriSource.
var bitmapImage = new BitmapImage();
myImage.Source = bitmapImage;
bitmapImage.UriSource = new URI("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
Esempio 2 code-behind (non valido): impostazione dell'UriSource di BitmapImage prima di connetterla all'albero.
var bitmapImage = new BitmapImage();
bitmapImage.UriSource = new URI("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
myImage.Source = bitmapImage;
Il pennello immagine non è rettangolare
Quando un'immagine viene usata per un pennello non rettangolare, l'immagine userà il percorso di rasterizzazione del software, che non scala affatto le immagini. Inoltre, deve archiviare una copia dell'immagine sia in memoria software che hardware. Ad esempio, se un'immagine viene usata come pennello per un'ellisse, l'immagine completa potenzialmente grande verrà archiviata due volte internamente. Quando si utilizza un pennello non rettangolare, l'app dovrebbe ridimensionare le immagini in anticipo, approssimativamente alla misura in cui verranno renderizzate.
In alternativa, puoi impostare una dimensione di decodifica esplicita per creare una versione dell'immagine con le dimensioni esatte in cui verrà disegnata sullo schermo usando le proprietà DecodePixelWidth e DecodePixelHeight.
<Image>
<Image.Source>
<BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
DecodePixelWidth="300" DecodePixelHeight="200"/>
</Image.Source>
</Image>
Le unità per DecodePixelWidth e DecodePixelHeight sono per impostazione predefinita pixel fisici. La proprietà DecodePixelType può essere usata per modificare questo comportamento: impostando DecodePixelType su Logica, le dimensioni della decodifica vengono automaticamente contabilizzate in base al fattore di scala corrente del sistema, in modo simile ad altri contenuti XAML. È quindi consigliabile impostare DecodePixelType su Logico se, ad esempio, si desidera DecodePixelWidth e DecodePixelHeight in modo che corrispondano alle proprietà Height e Width del controllo Image in cui verrà visualizzata l'immagine. Con il comportamento predefinito dell'uso dei pixel fisici, è necessario tenere conto del fattore di scala corrente del sistema e, nel caso in cui l'utente modifichi le preferenze di visualizzazione, è necessario ascoltare le notifiche di modifica della scala.
In alcuni casi in cui non è possibile determinare in anticipo una dimensione di decodifica appropriata, dovresti affidarti alla decodifica automatica delle giuste dimensioni di XAML, che tenterà con il massimo impegno di decodificare l'immagine alle dimensioni appropriate se non sono specificati esplicitamente DecodePixelWidth/DecodePixelHeight.
È consigliabile impostare una dimensione di decodifica esplicita se si conoscono le dimensioni del contenuto dell'immagine in anticipo. È anche consigliabile impostare DecodePixelType su Logico se le dimensioni di decodifica fornite sono relative ad altre dimensioni degli elementi XAML. Se ad esempio si impostano in modo esplicito le dimensioni del contenuto con Image.Width e Image.Height, è possibile impostare DecodePixelType su DecodePixelType.Logical per usare le stesse dimensioni logiche dei pixel di un controllo Image e quindi usare in modo esplicito BitmapImage.DecodePixelWidth e/o BitmapImage.DecodePixelHeight per controllare le dimensioni dell'immagine per ottenere un risparmio di memoria potenzialmente elevato.
Si noti che Image.Stretch deve essere considerato quando si determinano le dimensioni del contenuto decodificato.
Le immagini utilizzate in BitmapIcons tornano a decodificarsi alla dimensione naturale
Impostare una dimensione di decodifica esplicita per creare una versione dell'immagine con le dimensioni esatte che verrà disegnata sullo schermo usando le proprietà DecodePixelWidth e DecodePixelHeight.
Le immagini che appaiono estremamente grandi sullo schermo ritornano alla decodifica alla dimensione naturale
Le immagini che appaiono estremamente grandi sullo schermo vengono decodificate alla loro dimensione naturale. Impostare una dimensione di decodifica esplicita per creare una versione dell'immagine con le dimensioni esatte che verrà disegnata sullo schermo usando le proprietà DecodePixelWidth e DecodePixelHeight.
L'immagine è nascosta
L'immagine viene nascosta impostando l'opacità su 0 o la visibilità su Collapsed, nell'elemento immagine host, nel pennello, o in qualsiasi elemento padre. Le immagini non visibili sullo schermo a causa del ritaglio o della trasparenza possono essere decodificate alla loro dimensione naturale.
L'immagine usa la proprietà NineGrid
Quando un'immagine è utilizzata per un NineGrid, verrà impiegato un percorso di rasterizzazione software che non ridimensionerà affatto le immagini. Inoltre, deve archiviare una copia dell'immagine sia in memoria software che hardware. Quando si utilizza NineGrid, l'app dovrebbe pre-ridimensionare le sue immagini alle dimensioni approssimative a cui verranno renderizzate.
Le immagini che utilizzano la proprietà NineGrid tornano a decodificare alla dimensione naturale. Prendere in considerazione l'aggiunta dell'effetto ninegrid all'immagine originale.
DecodePixelWidth o DecodePixelHeight sono impostati su una dimensione maggiore di quella visualizzata sullo schermo
Se DecodePixelWidth/Height sono impostate in modo esplicito più grandi dell'immagine che verrà visualizzata sullo schermo, l'app userà inutilmente memoria aggiuntiva, fino a 4 byte per pixel, che diventa rapidamente oneroso per le immagini di grandi dimensioni. L'immagine verrà anche ridimensionata usando il ridimensionamento bilineare, il che potrebbe farla risultare sfocata se i fattori di scala sono grandi.
L'immagine viene decodificata come parte di un'immagine di trascina e rilascia
Impostare una dimensione di decodifica esplicita per creare una versione dell'immagine con le dimensioni esatte che verrà disegnata sullo schermo usando le proprietà DecodePixelWidth e DecodePixelHeight.
Elementi collassati in fase di caricamento
Un modello comune nelle app consiste nel nascondere inizialmente gli elementi nell'interfaccia utente e visualizzarli in un secondo momento. Nella maggior parte dei casi questi elementi devono essere posticipati usando x:Load o x:DeferLoadStrategy per evitare di pagare il costo di creazione dell'elemento in fase di caricamento.
Sono inclusi i casi in cui un convertitore booleano per la visibilità viene usato per nascondere gli elementi fino a un momento successivo.
Impatto
Gli elementi compressi vengono caricati insieme ad altri elementi e contribuiscono a un aumento del tempo di caricamento.
Motivo
Questa regola è stata attivata perché un elemento è stato compresso in fase di caricamento. Comprimere un elemento o impostarne l'opacità su 0, non impedisce la creazione dell'elemento. Questa regola può essere causata da un'app che usa un convertitore booleano per la visibilità che per impostazione predefinita è false.
Soluzione
Usando l'attributo x:Load o x:DeferLoadStrategy, è possibile ritardare il caricamento di una parte dell'interfaccia utente e caricarla quando necessario. Questo è un buon modo per ritardare l'elaborazione dell'interfaccia utente che non è visibile nel primo fotogramma. È possibile scegliere di caricare l'elemento quando necessario o come parte di un set di logica ritardata. Per attivare il caricamento, chiamare findName sull'elemento da caricare. x:Load estende le funzionalità di x:DeferLoadStrategy, consentendo il caricamento e lo scarico degli elementi, e il controllo dello stato di caricamento tramite x:Bind.
In alcuni casi, l'uso di findName per mostrare una parte dell'interfaccia utente potrebbe non essere la risposta. Questo vale se ci si aspetta di realizzare una parte significativa dell'interfaccia utente sul clic di un pulsante con una latenza molto bassa. In questo caso, potresti voler ottenere una latenza dell'interfaccia utente più veloce a scapito di memoria aggiuntiva; in tal caso, dovresti usare x:DeferLoadStrategy e impostare Visibility su Collapsed sull'elemento che desideri caricare. Dopo che la pagina è stata caricata e il thread dell'interfaccia utente è gratuito, è possibile chiamare findName quando necessario per caricare gli elementi. Gli elementi non saranno visibili all'utente finché non si imposta Visibility dell'elemento su Visible.
ListView non è virtualizzato
La virtualizzazione dell'interfaccia utente è il miglioramento più importante che è possibile apportare per migliorare le prestazioni della raccolta. Ciò significa che gli elementi dell'interfaccia utente che rappresentano gli elementi vengono creati su richiesta. Per un controllo elementi associato a una raccolta di 1000 elementi, si tratta di uno spreco di risorse per creare l'interfaccia utente per tutti gli elementi contemporaneamente perché non possono essere tutti visualizzati contemporaneamente. ListView e GridView (e altri controlli derivati da ItemsControl) eseguono la virtualizzazione dell'interfaccia utente. Quando gli elementi sono vicini a essere visualizzati durante lo scorrimento (a poche pagine di distanza), il framework genera l'interfaccia utente per gli elementi e li memorizza nella cache. Quando è improbabile che gli elementi vengano visualizzati di nuovo, il framework richiede nuovamente la memoria.
La virtualizzazione dell'interfaccia utente è solo uno dei diversi fattori chiave per migliorare le prestazioni della raccolta. La riduzione della complessità degli elementi di raccolta e della virtualizzazione dei dati sono due altri aspetti importanti per migliorare le prestazioni della raccolta. Per ulteriori informazioni sul miglioramento delle prestazioni di raccolta dati all'interno di ListViews e GridViews, vedere gli articoli sull'ottimizzazione dell'interfaccia utente ListView e GridView e la virtualizzazione dei dati di ListView e GridView.
Impatto
Un ItemsControl non virtualizzato aumenterà il tempo di caricamento e l'utilizzo delle risorse caricando più elementi figli del necessario.
Motivo
Il concetto di viewport è fondamentale per la virtualizzazione dell'interfaccia utente perché il framework deve creare gli elementi che è probabile che vengano visualizzati. In generale, il riquadro di visualizzazione di ItemsControl è l'estensione del controllo logico. Ad esempio, il riquadro di visualizzazione di un ListView è la larghezza e l'altezza dell'elemento ListView. Alcuni pannelli consentono spazio illimitato agli elementi figlio, come nel caso di ScrollViewer e del pannello Grid, con righe o colonne dimensionate automaticamente. Quando un ItemsControl virtualizzato viene posizionato in un pannello come quello, occupa spazio sufficiente per visualizzare tutti i suoi elementi, annullando così la virtualizzazione.
Soluzione
Ripristina la virtualizzazione impostando una larghezza e un'altezza sull'ItemsControl che stai utilizzando.
Thread dell'interfaccia utente bloccato o inattivo durante il caricamento
Il blocco del thread dell'interfaccia utente si riferisce alle chiamate sincrone a funzioni che eseguono fuori dal thread principale e bloccano il thread dell'interfaccia utente.
Per un elenco completo delle procedure consigliate per migliorare le prestazioni di avvio dell'app, vedi Procedure consigliate per le prestazioni di avvio dell'app e Mantenere reattivo il thread dell'interfaccia utente.
Impatto
Un thread dell'interfaccia utente bloccato o inattivo durante il caricamento impedirà il layout e altre operazioni dell'interfaccia utente, aumentando il tempo di avvio.
Motivo
Il codice della piattaforma per l'interfaccia utente e il codice dell'app per l'interfaccia utente vengono eseguiti nello stesso thread dell'interfaccia utente. Solo un'istruzione può essere eseguita su tale thread alla volta, quindi se il codice dell'app richiede troppo tempo per elaborare un evento, il framework non può eseguire il layout o generare nuovi eventi che rappresentano l'interazione dell'utente. La reattività dell'app è correlata alla disponibilità del thread dell'interfaccia utente per eseguire il lavoro.
Soluzione
L'app può essere interattiva anche se ci sono parti dell'app che non sono completamente funzionanti. Ad esempio, se l'app visualizza i dati che richiedono un po' di tempo per il recupero, puoi eseguire il codice indipendentemente 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. Per mantenere reattiva l'app, la piattaforma fornisce versioni asincrone di molte delle sue API. Un'API asincrona garantisce che il thread di esecuzione attivo non si blocchi mai per una quantità significativa di tempo. Quando chiami un'API dal thread dell'interfaccia utente, usa la versione asincrona, se disponibile.
{Binding} viene usato invece di {x:Bind}
Questa regola viene scatenata quando l'app usa un'istruzione {Binding}. Per migliorare le prestazioni delle app, le app devono prendere in considerazione l'uso di {x:Bind}.
Impatto
{Binding} richiede più tempo e più memoria di {x:Bind}.
Motivo
L'app usa {Binding} anziché {x:Bind}. {Binding} comporta un insieme di lavoro complesso e un sovraccarico della CPU. La creazione di un {Binding} causa una serie di allocazioni, e l'aggiornamento di un obiettivo di associazione può comportare riflessione e boxing.
Soluzione
Usare l'estensione di markup {x:Bind} che compila i binding in fase di compilazione. Le associazioni {x:Bind} (spesso definite associazioni compilate) offrono prestazioni elevate, offrono una convalida in fase di compilazione delle espressioni di binding e supportano il debug consentendo di impostare punti di interruzione nei file di codice generati come classe parziale per la pagina.
Si noti che x:Bind non è adatto in tutti i casi, ad esempio scenari con associazione tardiva. Per un elenco completo dei casi non coperti da {x:Bind}, vedere la documentazione di {x:Bind}.
x:Name viene usato invece di x:Key
ResourceDictionaries viene in genere usato per archiviare le risorse a livello globale, ovvero le risorse a cui l'app vuole fare riferimento in più posizioni; ad esempio stili, pennelli, modelli e così via. In generale, i ResourceDictionaries sono stati ottimizzati per non creare istanze delle risorse, a meno che non vengano richieste. Ma ci sono pochi posti in cui è necessario essere un po 'attenti.
Impatto
Qualsiasi risorsa con x:Name sarà istanziata quando 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.
Motivo
La tua app sta impostando x:Name su una risorsa.
Soluzione
Usare x:Key anziché x:Name quando non si fa riferimento alle risorse dal code-behind.
Il controllo delle raccolte utilizza un pannello non virtualizzante
Se fornisci un modello di pannello elementi personalizzato (vedi ItemsPanel), assicurati di usare un pannello di virtualizzazione, ad esempio ItemsWrapGrid o ItemsStackPanel. Se si usa VariableSizedWrapGrid, WrapGrid o StackPanel, non si otterrà la virtualizzazione. Inoltre, gli eventi ListView seguenti vengono generati solo quando si usano ItemsWrapGrid o ItemsStackPanel: ChoosingGroupHeaderContainer, ChoosingItemContainer e ContainerContentChanging.
La virtualizzazione dell'interfaccia utente è il miglioramento più importante che è possibile apportare per migliorare le prestazioni della raccolta. Ciò significa che gli elementi dell'interfaccia utente che rappresentano gli elementi vengono creati su richiesta. Per un controllo elementi associato a una raccolta di 1000 elementi, si tratta di uno spreco di risorse per creare l'interfaccia utente per tutti gli elementi contemporaneamente perché non possono essere tutti visualizzati contemporaneamente. ListView e GridView (e altri controlli derivati da ItemsControl) eseguono la virtualizzazione dell'interfaccia utente. Quando gli elementi sono vicini a essere visualizzati durante lo scorrimento (a poche pagine di distanza), il framework genera l'interfaccia utente per gli elementi e li memorizza nella cache. Quando è improbabile che gli elementi vengano visualizzati di nuovo, il framework richiede nuovamente la memoria.
La virtualizzazione dell'interfaccia utente è solo uno dei diversi fattori chiave per migliorare le prestazioni della raccolta. La riduzione della complessità degli elementi di raccolta e della virtualizzazione dei dati sono due altri aspetti importanti per migliorare le prestazioni della raccolta. Per ulteriori informazioni sul miglioramento delle prestazioni di raccolta dati all'interno di ListViews e GridViews, vedere gli articoli sull'ottimizzazione dell'interfaccia utente ListView e GridView e la virtualizzazione dei dati di ListView e GridView.
Impatto
Un ItemsControl non virtualizzato aumenterà il tempo di caricamento e l'utilizzo delle risorse caricando più elementi figli del necessario.
Motivo
Si usa un pannello che non supporta la virtualizzazione.
Soluzione
Usare un pannello di virtualizzazione, ad esempio ItemsWrapGrid o ItemsStackPanel.
Accessibilità: elementi UIA senza nome
In XAML puoi specificare un nome impostando AutomationProperties.Name. Molti peer di automazione forniscono un nome predefinito all'interfaccia utente se AutomationProperties.Name non è impostato.
Impatto
Se un utente raggiunge un elemento senza nome, spesso non avrà modo di sapere a cosa si riferisce l'elemento.
Motivo
Il nome dell'interfaccia utente dell'elemento è nullo o vuoto. Questa regola controlla ciò che vede l'UIA, non il valore di AutomationProperties.Name.
Soluzione
Impostare la proprietà AutomationProperties.Name nel codice XAML del controllo su una stringa localizzata appropriata.
A volte la correzione dell'applicazione non consiste nel fornire un nome, ma nel rimuovere l'elemento UIA da tutti gli alberi tranne quelli di base. Puoi farlo in XAML impostando AutomationProperties.AccessibilityView = "Raw"
.
Accessibilità: gli elementi dell'interfaccia utente con lo stesso tipo di controllo non devono avere lo stesso nome
Due elementi dell'interfaccia utente con lo stesso elemento padre UIA non devono avere lo stesso Nome e ControlType. È possibile avere due controlli con lo stesso nome se hanno controlType diversi.
Questa regola non controlla nomi duplicati con genitori diversi. Nella maggior parte dei casi, tuttavia, non è consigliabile duplicare i Nomi e i ControlTypes all'interno di un'intera finestra, anche con elementi padre diversi. I casi in cui i nomi duplicati all'interno di una finestra sono accettabili sono due elenchi con elementi identici. In questo caso, gli elementi dell'elenco devono avere nomi e ControlType identici.
Impatto
Se un utente raggiunge un elemento con lo stesso nome e ControlType di un altro elemento con lo stesso elemento padre dell'interfaccia utente, l'utente potrebbe non essere in grado di distinguere la differenza tra gli elementi.
Motivo
Gli elementi UIA con lo stesso elemento padre hanno lo stesso nome e ControlType.
Soluzione
Impostare un nome in XAML usando AutomationProperties.Name. Negli elenchi in cui si verifica in genere, usare l'associazione per associare il valore del AutomationProperties.Name a un'origine dati.