Sistema di layout

Aggiornamento: novembre 2007

In questo argomento viene illustrato il sistema di layout di Windows Presentation Foundation (WPF). È fondamentale comprendere come e quando vengono eseguiti i calcoli di layout per realizzare interfacce utente accativanti, intuitive e dalle prestazioni elevate.

Di seguito sono elencate le diverse sezioni di questo argomento.

Sistema di layout

Con il termine "layout" si intende descrivere il processo di misurazione e disposizione dei membri dell'insieme Children di un elemento Panel e di disegno degli stessi sullo schermo. Si tratta di un processo complesso e più grande è l'insieme Children, più elevato sarà il numero di calcoli necessari. La complessità può anche dipendere dal comportamento di layout definito dall'elemento Panel proprietario dell'insieme. Un layout relativamente semplice quale Canvas può offrire prestazioni ottimali quando un oggetto Panel più complesso quale Grid non è necessario.

Ogni volta che un oggetto UIElement figlio cambia posizione può potenzialmente attivare un nuovo passaggio da parte del sistema di layout. Pertanto, è importante comprendere gli eventi che possono richiamare il sistema di layout, poiché una chiamata non necessaria può influire negativamente sulle prestazioni dell'applicazione.

Nella sua forma più semplice, il layout è un sistema ricorsivo di ridimensionamento, posizionamento e disegno sullo schermo di un elemento. Il sistema di layout completa due passaggi per ogni membro dell'insieme Children, un passaggio di misurazione e un passaggio di disposizione. Ogni oggetto Panel figlio fornisce metodi MeasureOverride e ArrangeOverride specifici per ottenere un comportamento di layout specifico. Si tratta della serie di eventi che si verificano ogni volta che il sistema di layout viene richiamato.

  1. Il processo di layout di un oggetto UIElement figlio ha inizio con la misurazione delle relative proprietà principali.

  2. Vengono valutate le proprietà di ridimensionamento definite in FrameworkElement, ad esempio Width, Height e Margin.

  3. Viene applicata la logica specifica di Panel, ad esempio la direzione Dock o l'orientamento (Orientation) di sovrapposizione.

  4. Il contenuto viene disposto dopo la misurazione di tutti gli oggetti figlio.

  5. L'insieme Children viene disegnato sullo schermo.

  6. Il processo viene nuovamente richiamato se vengono aggiunti ulteriori elementi Children all'insieme, se viene applicata la proprietà LayoutTransform o se viene chiamato il metodo UpdateLayout.

Questo processo e i mezzi tramite i quali viene richiamato vengono illustrati nelle sezioni riportate di seguito.

Riquadri di elementi

Quando si pensa al layout di applicazioni in Windows Presentation Foundation (WPF), è importante comprendere il riquadro che racchiude tutti gli elementi. Questa astrazione è utile per comprendere il comportamento del sistema di layout. Ogni FrameworkElement utilizzato dal sistema di layout può essere inteso come un rettangolo "inciso" in una sezione del layout. Viene esposta una classe, LayoutInformation, affinché vengano restituiti i limiti geometrici dell'allocazione del layout di un elemento o slot. Le dimensioni del rettangolo sono determinate dal sistema sulla base dello spazio sullo schermo disponibile, delle dimensioni di eventuali vincoli, di proprietà specifiche del layout come margine e spaziatura interna e del comportamento dell'elemento Panel padre. Elaborando questi dati, il sistema è in grado di calcolare la posizione di tutti gli elementi figlio di un oggetto Panel specificato. È importante ricordare che le caratteristiche delle dimensioni definite sull'elemento padre, ad esempio Border, influiscono sui relativi elementi figlio.

Si consideri il semplice scenario di layout riportato di seguito.

Tipico oggetto Grid senza riquadro sovrapposto

Questo layout può essere ottenuto utilizzando il seguente codice Extensible Application Markup Language (XAML).

<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>

L'elemento TextBlock è ospitato in un oggetto Grid e il testo occupa solo l'angolo superiore sinistro della colonna in cui è stato inserito, anche se lo spazio allocato per l'oggetto TextBlock è effettivamente maggiore. Il riquadro di qualsiasi oggetto FrameworkElement può essere recuperato utilizzando il metodo GetLayoutSlot. Utilizzando questo metodo, il riquadro dell'elemento TextBlock è sovrapposto (questo è possibile perché l'oggetto TextBlock è ospitato all'interno di un oggetto Grid, un elemento Panel che consente la condivisione delle coordinate di layout).

Il riquadro di TextBlock è ora visibile

Come è ora evidente grazie alla linea bianca che la racchiude, la sezione allocata all'elemento TextBlock è effettivamente maggiore dello spazio che occupa. Con l'aggiunta di ulteriori elementi all'oggetto Grid, questa sezione potrà espandersi o ridursi, a seconda del tipo e delle dimensioni di tali elementi.

Dopo essere stato restituito, lo slot di layout dell'oggetto TextBlock viene convertito in un oggetto Path attraverso il metodo GetLayoutSlot, tecnica che può rivelarsi utile per la visualizzazione del riquadro di un elemento.

Private Sub getLayoutSlot1(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim myRectangleGeometry As New RectangleGeometry
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1)
    Dim myGeometryDrawing As New GeometryDrawing
    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
private void getLayoutSlot1(object sender, System.Windows.RoutedEventArgs e)
{
    RectangleGeometry myRectangleGeometry = new RectangleGeometry();
    myRectangleGeometry.Rect = LayoutInformation.GetLayoutSlot(txt1);
    GeometryDrawing myGeometryDrawing = new GeometryDrawing();
    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();
}

Misurazione e disposizione di elementi figlio

Quando viene eseguito il rendering del contenuto di un oggetto Window, il sistema di layout viene richiamato automaticamente. Per visualizzare contenuto, l'oggetto Content della finestra deve definire un elemento Panel radice allo scopo di definire un framework attraverso il quale gli elementi Children vengono organizzati sullo schermo. Per un elenco di elementi Panel disponibili e per informazioni sulla creazione di elementi di layout personalizzati, vedere Elementi Panel e comportamenti di layout personalizzati.

Il primo passaggio di layout è il passaggio di misurazione, durante il quale ogni membro dell'insieme Children viene valutato. Il processo ha inizio con una chiamata al metodo Measure. Questo metodo viene chiamato all'interno dell'implementazione dell'elemento Panel padre. Per eseguire il layout, non è necessario chiamare questo metodo in modo esplicito.

Innanzitutto vengono valutate le proprietà di dimensione native dell'oggetto UIElement, ad esempio Clip e Visibility. Viene generato un valore constraintSize che viene passato a MeasureCore.

In secondo luogo, vengono elaborate le proprietà di framework definite su FrameworkElement, con conseguente alterazione del valore di constraintSize. Queste proprietà tendono a descrivere le caratteristiche di ridimensionamento dell'elemento UIElement sottostante, ad esempio Height, Width, Margine Style. Ognuna di queste proprietà può modificare lo spazio necessario per visualizzare l'elemento. Il metodo 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 basato su altri input di altezza e sul sistema di layout. Il valore viene impostato dal sistema di layout, sulla base di un passaggio di rendering effettivo e pertanto potrebbe rimanere indietro rispetto al valore impostato di proprietà quali Height che sono la base della modifica dell'input.

Poiché ActualHeight è un valore calcolato, tenere presente che potrebbe subire più modifiche o modifiche incrementali in seguito a varie operazioni eseguite dal sistema di layout. Il sistema di layout potrebbe calcolare lo spazio di misurazione necessario per gli elementi figlio, i vincoli imposti dall'elemento padre e così via.

L'obiettivo finale del passaggio di misurazione consiste nel determinare per l'elemento figlio la proprietà DesiredSize, operazione che viene eseguita durante la chiamata al metodo MeasureCore. Questo valore viene archiviato dal metodo Measure e verrà utilizzato durante il processo di disposizione del contenuto.

Il processo di disposizione ha inizio con una chiamata al metodo Arrange. Durante il passaggio di disposizione, l'elemento Panel padre genera un rettangolo che rappresenta i limiti dell'elemento figlio. Questo valore viene passato al metodo ArrangeCore affinché venga elaborato.

Il metodo ArrangeCore valuta la proprietà DesiredSize dell'elemento figlio ed eventuali margini aggiuntivi che potrebbero influire sulle dimensioni sottoposte a rendering dell'elemento, quindi genera un valore arrangeSize che viene passato al metodo ArrangeOverride dell'oggetto Panel come parametro. Il metodo ArrangeOverride genera il valore finalSize dell'elemento figlio e infine il metodo ArrangeCore esegue una valutazione finale delle proprietà di offset quali margine e allineamento e inserisce l'elemento figlio nel relativo slot di layout. L'elemento figlio non deve riempire (e spesso non lo farà) l'intero spazio allocato. Quando il controllo viene restituito all'elemento Panel, il processo di layout può essere considerato completato.

Elementi Panel e comportamenti di layout personalizzati

Windows Presentation Foundation (WPF) include un insieme derivato di elementi Panel con i quali è possibile definire molti layout complessi. Tramite l'elemento StackPanel è possibile realizzare con facilità scenari comuni quali la sovrapposizione di elementi, mentre layout più complessi e dinamici possono essere ottenuti utilizzando l'elemento Canvas.

Nella tabella riportata di seguito vengono riepilogati gli elementi di layout disponibili.

Nome Panel

Descrizione

Canvas

Definisce un'area nella quale è possibile posizionare in modo esplicito gli elementi figlio utilizzando coordinate relative all'area Canvas.

DockPanel

Definisce un'area all'interno della quale è possibile disporre gli elementi figlio orizzontalmente o verticalmente, l'uno rispetto all'altro.

Grid

Definisce un'area flessibile della griglia costituita da colonne e righe.

StackPanel

Dispone gli elementi figlio in una singola riga che può essere orientata orizzontalmente o verticalmente.

VirtualizingPanel

Fornisce un framework per gli elementi Panel che "virtualizzano" la raccolta dati dell'elemento figlio. Questa è una classe astratta.

WrapPanel

Posiziona gli elementi figlio in sequenza da sinistra verso destra, interrompendo il contenuto quando viene raggiunto il bordo della casella contenitore e facendolo ripartire dalla riga successiva. Successivamente l'ordinamento in sequenza procede dall’alto verso il basso o da destra verso sinistra, a seconda del valore della proprietà Orientation.

Per esempi di codice che illustrano l'utilizzo di ognuno di questi elementi, vedere Esempi di layout.

Per scenari che richiedono un layout dell'applicazione impossibile da realizzare utilizzando questi elementi Panel predefiniti, è possibile ottenere comportamenti di layout personalizzati ereditando da Panel ed eseguendo l'override dei metodi MeasureOverride e ArrangeOverride. Per un esempio, vedere Esempio di pannello radiale personalizzato.

Considerazioni sulle prestazioni correlate al layout

Il layout è un processo ricorsivo. Ogni elemento figlio di un insieme Children viene elaborato durante ogni chiamata del sistema. Di conseguenza, evitare di attivare il sistema quando non è necessario. Attenendosi ai suggerimenti riportati di seguito, sarà possibile ottenere prestazioni ottimali.

Le proprietà di dipendenza i cui valori possono causare l'inizializzazione del sistema di layout sono contrassegnate con flag pubblici. AffectsMeasure e AffectsArrange indicano quali modifiche apportate ai valori di proprietà imporranno al sistema di layout di eseguire un aggiornamento ricorsivo. In generale, per qualsiasi proprietà che può influire sulle dimensioni del riquadro di un elemento, il flag AffectsMeasure deve essere impostato su true. Per ulteriori informazioni, vedere Cenni preliminari sulle proprietà di dipendenza.

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

Evitare chiamate non necessarie al metodo UpdateLayout. Questo metodo impone un aggiornamento ricorsivo del layout e spesso non è necessario. A meno che non si sia certi della reale necessità di un aggiornamento completo, lasciare che sia il sistema di layout a chiamare questo metodo.

In presenza di un insieme Children di grandi dimensioni, considerare l'utilizzo dell'oggetto VirtualizingStackPanel anziché di un normale StackPanel. Tramite la "virtualizzazione" dell'insieme figlio, VirtualizingStackPanel mantiene in memoria solo gli oggetti che attualmente si trovano all'interno delRiquadro di visualizzazione dell'elemento padre. Di conseguenza, nella maggior parte degli scenari le prestazioni risultano notevolmente migliorate.

Argomenti successivi

La comprensione dei processi di misurazione e disposizione degli elementi è fondamentale per comprendere il layout come sistema. Per un approfondimento degli elementi Panel disponibili, vedere Cenni preliminari sugli elementi Panel. Per comprendere meglio le varie proprietà di posizionamento che possono influire sul layout, vedere Panoramica su allineamento, margini e spaziatura interna. Per un esempio di elemento Panel personalizzato, vedere Esempio di pannello radiale personalizzato. Per mettere in pratica tutte queste nozioni realizzando una semplice applicazione, vedere Guida introduttiva a Windows Presentation Foundation.

Vedere anche

Concetti

Cenni preliminari sugli elementi Panel

Panoramica su allineamento, margini e spaziatura interna

Blocco dei pixel nelle applicazioni WPF

XAMLPad

Ottimizzazione delle prestazioni: layout e progettazione

Riferimenti

FrameworkElement

UIElement