Condividi tramite


Impaginazione

In questo argomento viene descritto il sistema di layout Windows Presentation Foundation (WPF). Comprendere come e quando si verificano i calcoli del layout è essenziale per la creazione di interfacce utente in WPF.

Questo argomento contiene le sezioni seguenti:

Rettangoli di delimitazione degli elementi

Quando si pensa al layout in WPF, è importante comprendere il contenitore che racchiude tutti gli elementi. Ogni FrameworkElement utilizzato dal sistema di layout può essere considerato come un rettangolo inserito nel layout. La classe LayoutInformation restituisce i limiti dell'allocazione del layout o dello slot di un elemento. Le dimensioni del rettangolo sono determinate calcolando lo spazio dello schermo disponibile, le dimensioni di tutti i vincoli, le proprietà specifiche del layout (ad esempio margine e spaziatura interna) e il comportamento individuale dell'elemento padre Panel. Elaborando questi dati, il sistema di layout è in grado di calcolare la posizione di tutti gli elementi figlio di un determinato Panel. È importante ricordare che le caratteristiche di ridimensionamento definite nell'elemento padre, ad esempio un Border, influiscono sui relativi elementi figlio.

La figura seguente mostra un layout semplice.

Screenshot che mostra una griglia tipica, senza rettangolo delimitatore sovrapposto.

Questo layout può essere ottenuto usando il codice XAML seguente.

<Grid Name="myGrid" Background="LightSteelBlue" Height="150">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="250"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <TextBlock Name="txt1" Margin="5" FontSize="16" FontFamily="Verdana" Grid.Column="0" Grid.Row="0">Hello World!</TextBlock>
  <Button Click="getLayoutSlot1" Width="125" Height="25" Grid.Column="0" Grid.Row="1">Show Bounding Box</Button>
  <TextBlock Name="txt2" Grid.Column="1" Grid.Row="2"/>
</Grid>

Un singolo elemento TextBlock è ospitato all'interno di un Grid. Mentre il testo riempie solo l'angolo superiore sinistro della prima colonna, lo spazio allocato per il TextBlock è effettivamente molto più grande. La scatola delimitatrice di qualsiasi FrameworkElement può essere ottenuta utilizzando il metodo GetLayoutSlot. Nella figura seguente viene illustrato il rettangolo di delimitazione per l'elemento TextBlock.

Screenshot che mostra che il riquadro di delimitazione del TextBlock è ora visibile.

Come illustrato dal rettangolo giallo, lo spazio allocato per l'elemento TextBlock è effettivamente molto più grande di quello visualizzato. Man mano che vengono aggiunti elementi aggiuntivi alla Grid, questa allocazione potrebbe ridursi o espandersi, a seconda del tipo e delle dimensioni degli elementi aggiunti.

Lo slot di layout del TextBlock è tradotto in un Path utilizzando il metodo GetLayoutSlot. Questa tecnica può essere utile per visualizzare la scatola di delimitazione di un elemento.

private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    Path myPath = new Path();
    myPath.Data = myRectangleGeometry;
    myPath.Stroke = Brushes.LightGoldenrodYellow;
    myPath.StrokeThickness = 5;
    Grid.SetColumn(myPath, 0);
    Grid.SetRow(myPath, 0);
    myGrid.Children.Add(myPath);
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString();
}
Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    Dim myPath As New Path
    myPath.Data = myRectangleGeometry
    myPath.Stroke = Brushes.LightGoldenrodYellow
    myPath.StrokeThickness = 5
    Grid.SetColumn(myPath, 0)
    Grid.SetRow(myPath, 0)
    myGrid.Children.Add(myPath)
    txt2.Text = "LayoutSlot is equal to " + LayoutInformation.GetLayoutSlot(txt1).ToString()
End Sub

Sistema di layout

Nel suo aspetto più semplice, il layout è un sistema ricorsivo che porta a dimensionare, posizionare e disegnare un elemento. In particolare, il layout descrive il processo di misurazione e disposizione dei membri della raccolta Children di un elemento Panel. Il layout è un processo impegnativo. Maggiore è la raccolta di Children, maggiore è il numero di calcoli da eseguire. La complessità può essere introdotta anche in base al comportamento di layout definito dall'elemento Panel proprietario della raccolta. Un Panelrelativamente semplice, ad esempio Canvas, può avere prestazioni significativamente migliori rispetto a un Panelpiù complesso, ad esempio Grid.

Ogni volta che un figlio UIElement cambia posizione, può potenzialmente attivare un nuovo passaggio nel sistema di layout. È quindi importante comprendere gli eventi che possono richiamare il sistema di layout, perché la chiamata non necessaria può causare prestazioni dell'applicazione scarse. Di seguito viene descritto il processo che si verifica quando viene richiamato il sistema di layout.

  1. Il bambino UIElement inizia il processo di layout avendo prima le sue proprietà principali misurate.

  2. Le proprietà di ridimensionamento definite nei FrameworkElement vengono valutate, come ad esempio Width, Heighte Margin.

  3. Panelviene applicata una logica specifica, come la direzione Dock o l'impilamento Orientation.

  4. Il contenuto viene disposto dopo che tutti gli elementi figli sono stati misurati.

  5. La raccolta Children viene disegnata sullo schermo.

  6. Il processo viene richiamato di nuovo se alla raccolta vengono aggiunti altri Children, viene applicato un LayoutTransform o viene chiamato il metodo UpdateLayout.

Questo processo e il modo in cui viene richiamato vengono definiti in modo più dettagliato nelle sezioni seguenti.

Misurazione e disposizione dei bambini

Il sistema di layout completa due passaggi per ogni membro della raccolta Children, un passaggio di misura e un passaggio di disposizione. Ogni figlio Panel fornisce i propri metodi MeasureOverride e ArrangeOverride per raggiungere uno specifico comportamento di layout.

Durante la fase di misurazione, ogni membro della raccolta Children viene valutato. Il processo inizia con una chiamata al metodo Measure. Questo metodo viene chiamato all'interno dell'implementazione dell'elemento padre Panel e non deve essere chiamato in modo esplicito affinché venga eseguito il layout.

Prima di tutto, vengono valutate le proprietà delle dimensioni native del UIElement, ad esempio Clip e Visibility. Viene generato un valore denominato constraintSize passato a MeasureCore.

In secondo luogo, vengono elaborate le proprietà del framework definite in FrameworkElement, il che influisce sul valore di constraintSize. Queste proprietà descrivono in genere le caratteristiche di ridimensionamento del UIElementsottostante, come Height, Width, Margine Style. Ognuna di queste proprietà può modificare lo spazio necessario per visualizzare l'elemento. MeasureOverride viene quindi chiamato con constraintSize come parametro.

Nota

Esiste una differenza tra le proprietà di Height e Width e ActualHeight e ActualWidth. Ad esempio, la proprietà ActualHeight è un valore calcolato in base ad altri input di altezza e al sistema di layout. Il valore viene impostato dal sistema di layout stesso, in base a un passaggio di rendering reale, e può quindi essere leggermente in ritardo rispetto al valore impostato delle proprietà, come Height, che costituiscono la base della variazione dell'input.

Poiché ActualHeight è un valore calcolato, è necessario tenere presente che potrebbero essere presenti più modifiche segnalate o incrementali a esso in seguito a varie operazioni dal sistema di layout. Il sistema di layout può calcolare lo spazio di misura necessario per gli elementi figlio, i vincoli imposti dall'elemento padre e così via.

L'obiettivo finale del passaggio di misura è che il bambino determini il suo DesiredSize, che si verifica durante la chiamata MeasureCore. Il valore DesiredSize viene archiviato da Measure per l'uso durante il passaggio di disposizione del contenuto.

Il passaggio di arrangiamento inizia con una chiamata al metodo Arrange. Durante il passaggio di disposizione, l'elemento padre Panel genera un rettangolo che rappresenta i limiti dell'elemento figlio. Questo valore viene passato al metodo ArrangeCore per l'elaborazione.

Il metodo ArrangeCore valuta la DesiredSize del figlio e analizza eventuali margini aggiuntivi che possono influire sulla dimensione resa dell'elemento. ArrangeCore genera un arrangeSize, che viene passato come parametro al metodo ArrangeOverride del Panel. ArrangeOverride genera il finalSize del figlio. Infine, il metodo ArrangeCore esegue una valutazione finale delle proprietà di offset, come il margine e l'allineamento, e posiziona l'elemento figlio all'interno del suo spazio di layout. Il bambino non deve (e spesso non lo fa) riempire l'intero spazio allocato. Il controllo viene quindi restituito al padre Panel e il processo di layout è completo.

Elementi del pannello e comportamenti di layout personalizzati

WPF include un gruppo di elementi che derivano da Panel. Questi elementi Panel consentono molti layout complessi. Ad esempio, è possibile posizionare facilmente elementi in pila usando l'elemento StackPanel, mentre layout più complessi e a flusso libero sono possibili usando l'elemento Canvas.

La tabella seguente riepiloga gli elementi Panel di layout disponibili.

Nome pannello Descrizione
Canvas Definisce un'area dentro la quale puoi posizionare esplicitamente gli elementi figli mediante coordinate relative all'area Canvas.
DockPanel Definisce un'area all'interno della quale è possibile disporre gli elementi figlio orizzontalmente o verticalmente, rispetto l'uno all'altro.
Grid Definisce un'area della griglia flessibile costituita da colonne e righe.
StackPanel Dispone gli elementi figlio in una singola linea che può essere orientata orizzontalmente o verticalmente.
VirtualizingPanel Fornisce un framework per gli elementi Panel che virtualizzano la raccolta dati dei figli. Si tratta di una classe astratta.
WrapPanel Posiziona gli elementi figlio in posizione sequenziale da sinistra a destra, interrompendo il contenuto alla riga successiva al bordo della casella contenitore. L'ordinamento successivo viene eseguito in sequenza dall'alto verso il basso o da destra a sinistra, a seconda del valore della proprietà Orientation.

Per le applicazioni che richiedono un layout che non è possibile usando uno degli elementi Panel predefiniti, è possibile ottenere comportamenti di layout personalizzati ereditando da Panel ed eseguendo l'override dei metodi MeasureOverride e ArrangeOverride.

Considerazioni sulle prestazioni del layout

Il layout è un processo ricorsivo. Ogni elemento figlio in una raccolta Children viene elaborato durante ogni invocazione del sistema di layout. Di conseguenza, l'attivazione del sistema di layout deve essere evitata quando non è necessaria. Le considerazioni seguenti consentono di ottenere prestazioni migliori.

  • Tenere presente quali modifiche al valore della proprietà comporteranno un aggiornamento ricorsivo dal sistema di layout.

    Le proprietà di dipendenza i cui valori possono causare l'inizializzazione del sistema di layout sono contrassegnate con flag pubblici. AffectsMeasure e AffectsArrange forniscono utili indizi su quali modifiche al valore della proprietà forzano un aggiornamento ricorsivo dal sistema di layout. In generale, qualsiasi proprietà che può influire sulle dimensioni del rettangolo di selezione di un elemento deve avere un flag AffectsMeasure impostato su true. Per ulteriori informazioni, consultare Panoramica delle proprietà dipendenti.

  • Quando possibile, usare un RenderTransform anziché un LayoutTransform.

    Un LayoutTransform può essere un modo molto utile per influire sul contenuto di un'interfaccia utente. Tuttavia, se l'effetto della trasformazione non deve influire sulla posizione di altri elementi, è preferibile usare una RenderTransform, perché RenderTransform non richiama il sistema di layout. LayoutTransform applica la trasformazione e forza un aggiornamento ricorsivo del layout per tenere conto della nuova posizione dell'elemento interessato.

  • Evitare chiamate non necessarie a UpdateLayout.

    Il metodo UpdateLayout forza un aggiornamento ricorsivo del layout e spesso non è necessario. A meno che non si sia certi che sia necessario un aggiornamento completo, fare affidamento sul sistema di layout per chiamare automaticamente questo metodo.

  • Quando si usa una grande raccolta di Children, è consigliabile usare un VirtualizingStackPanel anziché un normale StackPanel.

    Virtualizzando la raccolta figlia, il VirtualizingStackPanel mantiene in memoria solo gli oggetti attualmente presente all'interno del viewport dell'elemento padre. Di conseguenza, le prestazioni sono notevolmente migliorate nella maggior parte degli scenari.

Rendering sottopixel e arrotondamento del layout

Il sistema grafico WPF usa unità indipendenti dal dispositivo per abilitare la risoluzione e l'indipendenza del dispositivo. Ogni pixel indipendente dal dispositivo si adatta automaticamente all'impostazione dei punti per pollice (dpi) del sistema. Questo fornisce alle applicazioni WPF un corretto ridimensionamento per diverse impostazioni di dpi e le rende automaticamente consapevoli del dpi.

Tuttavia, l'indipendenza dai dpi può creare rendering dei bordi irregolari a causa dell'anti-aliasing. Questi artefatti, in genere visti come bordi sfocati o semitrasparenti, possono verificarsi quando la posizione di un bordo cade al centro di un pixel del dispositivo anziché tra i pixel del dispositivo. Il sistema di layout offre un modo per adattarsi a questo con l'arrotondamento del layout. L'arrotondamento del layout è il processo in cui il sistema di layout arrotonda tutti i valori di pixel non integrali durante la fase di layout.

L'arrotondamento del layout è disabilitato per impostazione predefinita. Per abilitare l'arrotondamento del layout, impostare la proprietà UseLayoutRounding su true in qualsiasi FrameworkElement. Poiché si tratta di una proprietà di dipendenza, il valore verrà propagato a tutti gli elementi figlio nella struttura ad albero visuale. Per abilitare l'arrotondamento del layout dell'intera interfaccia utente, impostare UseLayoutRounding a true nel contenitore radice. Per un esempio, vedere UseLayoutRounding.

Cosa succederà adesso

Comprendere come vengono misurati e disposti gli elementi è il primo passaggio per comprendere il layout. Per maggiori informazioni sugli elementi disponibili di Panel, vedere la sezione Panoramica dei pannelli . Per comprendere meglio le varie proprietà di posizionamento che possono influire sul layout, vedere Panoramica di allineamento, margini e spaziatura interna. Quando sei pronto a mettere tutto insieme in un'applicazione leggera, consultare Guida: La mia prima applicazione desktop WPF.

Vedere anche