Ottimizzare il layout XAML

API importanti

Il layout è il processo di definizione della struttura visiva dell'interfaccia utente. Il meccanismo principale per la descrizione del layout in XAML usa i pannelli, che sono oggetti contenitore in cui puoi posizionare e disporre gli elementi dell'interfaccia utente. Il layout può essere una parte dispendiosa di un'app XAML, per quanto riguarda uso della CPU e sovraccarico della memoria. Ecco alcuni semplici passaggi per migliorare le prestazioni di layout nella tua app XAML.

Ridurre la struttura di layout

Il miglioramento principale per le prestazioni di layout viene realizzato semplificando l'organizzazione gerarchica della struttura ad albero degli elementi dell'interfaccia utente. I pannelli sono inclusi nella struttura ad albero visuale, ma sono elementi strutturali e non elementi che producono pixel come Button o Rectangle. La semplificazione della struttura ad albero attraverso la riduzione del numero di elementi che non producono pixel offre in genere un miglioramento significativo delle prestazioni.

Molte interfacce utente vengono implementate tramite l'annidamento di pannelli, che produce strutture ad albero di pannelli ed elementi complesse e ingombranti. L'annidamento dei pannelli è efficace, ma in molti casi la stessa interfaccia utente può essere ottenuta con un unico pannello più complesso. L'uso di un unico pannello offre prestazioni migliori.

Quando ridurre la struttura di layout

La riduzione della struttura di layout in modo banale, ad esempio riducendo un pannello annidato dalla pagina di primo livello, non ha un effetto visibile.

Le prestazioni migliori si ottengono riducendo la struttura di layout ripetuta nell’interfaccia utente, ad esempio in un elemento ListView o GridView. Questi elementi ItemsControl usano un oggetto DataTemplate, che definisce un sottoalbero di elementi dell’interfaccia utente di cui viene creata un’istanza molte volte. Quando lo stesso sottoalbero viene duplicato molte volte nell'app, qualsiasi miglioramento delle prestazioni di questo sottoalbero ha un effetto esponenziale sulle prestazioni complessive della tua app.

Esempi

Considerare l'interfaccia utente seguente.

Form layout example

Questi esempi mostrano tre modi di implementare la stessa interfaccia utente. Ognuna delle scelte di implementazione produce pixel quasi identici sullo schermo, ma differisce sostanzialmente nei dettagli di implementazione.

Opzione 1: elementi StackPanel annidati

Nonostante sia il modello più semplice, usa 5 elementi pannello e produce un sovraccarico significativo.

  <StackPanel>
  <TextBlock Text="Options:" />
  <StackPanel Orientation="Horizontal">
      <CheckBox Content="Power User" />
      <CheckBox Content="Admin" Margin="20,0,0,0" />
  </StackPanel>
  <TextBlock Text="Basic information:" />
  <StackPanel Orientation="Horizontal">
      <TextBlock Text="Name:" Width="75" />
      <TextBox Width="200" />
  </StackPanel>
  <StackPanel Orientation="Horizontal">
      <TextBlock Text="Email:" Width="75" />
      <TextBox Width="200" />
  </StackPanel>
  <StackPanel Orientation="Horizontal">
      <TextBlock Text="Password:" Width="75" />
      <TextBox Width="200" />
  </StackPanel>
  <Button Content="Save" />
</StackPanel>

Opzione 2: un singolo Grid

L’elemento Grid aggiunge una certa complessità, ma usa un solo elemento pannello.

<Grid>
  <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="Auto" />
  </Grid.ColumnDefinitions>
  <TextBlock Text="Options:" Grid.ColumnSpan="2" />
  <CheckBox Content="Power User" Grid.Row="1" Grid.ColumnSpan="2" />
  <CheckBox Content="Admin" Margin="150,0,0,0" Grid.Row="1" Grid.ColumnSpan="2" />
  <TextBlock Text="Basic information:" Grid.Row="2" Grid.ColumnSpan="2" />
  <TextBlock Text="Name:" Width="75" Grid.Row="3" />
  <TextBox Width="200" Grid.Row="3" Grid.Column="1" />
  <TextBlock Text="Email:" Width="75" Grid.Row="4" />
  <TextBox Width="200" Grid.Row="4" Grid.Column="1" />
  <TextBlock Text="Password:" Width="75" Grid.Row="5" />
  <TextBox Width="200" Grid.Row="5" Grid.Column="1" />
  <Button Content="Save" Grid.Row="6" />
</Grid>

Opzione 3: un unico elemento RelativePanel:

Anche questo unico pannello è leggermente più complesso in confronto all’uso di pannelli annidati, ma può rivelarsi più facile da comprendere e gestire rispetto a un elemento Grid.

<RelativePanel>
  <TextBlock Text="Options:" x:Name="Options" />
  <CheckBox Content="Power User" x:Name="PowerUser" RelativePanel.Below="Options" />
  <CheckBox Content="Admin" Margin="20,0,0,0" 
            RelativePanel.RightOf="PowerUser" RelativePanel.Below="Options" />
  <TextBlock Text="Basic information:" x:Name="BasicInformation"
           RelativePanel.Below="PowerUser" />
  <TextBlock Text="Name:" RelativePanel.AlignVerticalCenterWith="NameBox" />
  <TextBox Width="200" Margin="75,0,0,0" x:Name="NameBox"               
           RelativePanel.Below="BasicInformation" />
  <TextBlock Text="Email:"  RelativePanel.AlignVerticalCenterWith="EmailBox" />
  <TextBox Width="200" Margin="75,0,0,0" x:Name="EmailBox"
           RelativePanel.Below="NameBox" />
  <TextBlock Text="Password:" RelativePanel.AlignVerticalCenterWith="PasswordBox" />
  <TextBox Width="200" Margin="75,0,0,0" x:Name="PasswordBox"
           RelativePanel.Below="EmailBox" />
  <Button Content="Save" RelativePanel.Below="PasswordBox" />
</RelativePanel>

Come mostra l'esempio, esistono molti modi di realizzare la stessa interfaccia utente. La scelta deve essere determinata da un'attenta considerazione di tutti i compromessi, ad esempio riguardo a prestazioni, leggibilità e gestibilità.

Usare griglie a una cella per gli elementi dell'interfaccia utente che si sovrappongono

Un requisito comune per l'interfaccia utente consiste nel creare un layout in cui gli elementi si sovrappongono gli uni agli altri. Per posizionare gli elementi in questo modo, vengono usati in genere spaziatura interna, margini, allineamenti e trasformazioni. Il controllo Grid XAML è ottimizzato per migliorare le prestazioni di layout per gli elementi che si sovrappongono.

ImportantePer osservare un miglioramento, usa un elemento Grid a una cella. Non definire RowDefinitions o ColumnDefinitions.

Esempi

<Grid>
    <Ellipse Fill="Red" Width="200" Height="200" />
    <TextBlock Text="Test" 
               HorizontalAlignment="Center" 
               VerticalAlignment="Center" />
</Grid>

Text overlaid on a circle

<Grid Width="200" BorderBrush="Black" BorderThickness="1">
    <TextBlock Text="Test1" HorizontalAlignment="Left" />
    <TextBlock Text="Test2" HorizontalAlignment="Right" />
</Grid>

Two text blocks in a grid

Usare le proprietà del bordo predefinito di un pannello

I controlli Grid, StackPanel, RelativePanel e ContentPresenter hanno proprietà per un bordo predefinito che ti permettono di disegnare un bordo attorno a tali controlli senza dover aggiungere un altro elemento Border al codice XAML. Le nuove proprietà che supportano il bordo predefinito sono: BorderBrush, BorderThickness, CornerRadiuse Padding. Dato che ognuna di queste proprietà è un oggetto DependencyProperty, possono essere usate con associazioni e animazioni. Le proprietà sono progettate per sostituire completamente un elemento Border separato.

Se l’interfaccia utente ha elementi Border attorno ai pannelli, usare il bordo predefinito al loro posto, in modo da fare a meno di un elemento aggiuntivo nella struttura di layout dell’app. Come accennato prima, può trattarsi di un risparmio significativo, in particolare in caso di interfaccia utente ripetuta.

Esempi

<RelativePanel BorderBrush="Red" BorderThickness="2" CornerRadius="10" Padding="12">
    <TextBox x:Name="textBox1" RelativePanel.AlignLeftWithPanel="True"/>
    <Button Content="Submit" RelativePanel.Below="textBox1"/>
</RelativePanel>

Usare eventi SizeChanged per rispondere alle modifiche di layout

La classe FrameworkElement espone due eventi simili per rispondere alle modifiche di layout: LayoutUpdated e SizeChanged. Si potrebbe usare uno di questi eventi per ricevere una notifica quando un elemento viene ridimensionato durante il layout. La semantica dei due eventi è diversa e nello scegliere quale usare si dovrà valutare alcune considerazioni importanti sulle prestazioni.

Per ottenere buone prestazioni, SizeChanged è quasi sempre la scelta giusta. SizeChanged ha una semantica intuitiva. Questo oggetto viene generato durante il layout una volta aggiornate le dimensioni di FrameworkElement.

Anche LayoutUpdated viene generato durante il layout, ma ha una semantica globale, ovvero viene generato su ogni elemento all'aggiornamento dell'elemento. L'elaborazione locale viene eseguita in genere nel gestore dell'evento e in questo caso il codice viene eseguito più spesso del necessario. Usare LayoutUpdated solo se si deve poter determinare il momento in cui un elemento viene riposizionato senza alcuna modifica delle dimensioni (che è un caso poco comune).

Scelta tra pannelli

In genere, le prestazioni non vengono considerate quando si sceglie tra singoli pannelli. Questa scelta è normalmente determinata dalla valutazione riguardo ai pannelli che forniscono il comportamento di layout più vicino all'interfaccia utente implementata. Ad esempio, se si sceglie tra Grid, StackPanel e RelativePanel, optare per il pannello che fornisce il mapping più simile al proprio modello mentale dell’implementazione.

Ogni pannello XAML è ottimizzato per garantire buone prestazioni e tutti i pannelli offrono prestazioni equivalenti per interfacce utente simili.