Sistema di layout

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 la creazione di interfacce utente in WPF.

In questo argomento sono incluse le sezioni seguenti:

  • Riquadri degli elementi

  • Sistema di layout

  • Misurazione e disposizione di elementi figlio

  • Elementi Panel e comportamenti di layout personalizzati

  • Considerazioni sulle prestazioni correlate al layout

  • Rendering dei subpixel e arrotondamento del layout

  • Fasi successive

Riquadri degli elementi

Nella valutazione del layout di WPF è importante tenere presente il riquadro che racchiude tutti gli elementi. Ogni oggetto FrameworkElement utilizzato dal sistema di layout può essere considerato come un rettangolo inciso nel layout. La classe LayoutInformation restituisce i limiti dell'allocazione del layout di un elemento o slot. Le dimensioni del rettangolo vengono determinate dal sistema in base allo spazio disponibile sullo schermo, alle dimensioni di eventuali vincoli, a proprietà specifiche del layout, ad esempio margine e spaziatura interna, e al comportamento dell'elemento Panel padre. L'elaborazione di questi dati consente al sistema di layout di calcolare la posizione di tutti gli elementi figlio di un determinato oggetto Panel. È importante ricordare che le caratteristiche delle dimensioni definite sull'elemento padre, ad esempio Border, influiscono sui relativi elementi figlio.

Nell'immagine seguente viene illustrato un layout semplice.

Tipico oggetto Grid senza riquadro sovrapposto

È possibile ottenere questo layout utilizzando 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 oggetto Grid. Mentre il testo riempie solo l'angolo superiore sinistro della prima colonna, lo spazio allocato per TextBlock è effettivamente più grande. Il riquadro di un oggetto FrameworkElement può essere recuperato utilizzando il metodo GetLayoutSlot. Nella figura seguente viene illustrato il riquadro dell'elemento TextBlock.

Il riquadro di TextBlock è ora visibile

Come indicato dal rettangolo giallo, lo spazio allocato per l'elemento TextBlock è effettivamente più grande di quanto non appaia. 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.

Lo slot di layout di TextBlock viene convertito in un oggetto Path tramite il metodo GetLayoutSlot. Questa tecnica può essere utile per visualizzare il 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();
}

Sistema di layout

Nella sua forma più semplice il layout è un sistema ricorsivo di ridimensionamento, posizionamento e disegno di un elemento. In modo più specifico, con il termine layout si intende descrivere il processo di misurazione e disposizione dei membri dell'insieme Children di un elemento Panel. Il layout è un processo intensivo. Più grande è l'insieme Children, maggiore è il numero di calcoli da eseguire. La complessità può dipendere anche dal comportamento di layout definito dall'elemento Panel proprietario dell'insieme. Le prestazioni di un oggetto Panel relativamente semplice, ad esempio Canvas, possono essere significativamente migliori di quelle di un oggetto Panel più complesso, ad esempio Grid.

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. Di seguito viene descritto il processo che si verifica quando viene richiamato il sistema di layout.

  1. Un oggetto UIElement figlio avvia innanzitutto il processo di layout 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 il modo in cui viene richiamato vengono illustrati in dettaglio nelle sezioni seguenti.

Misurazione e disposizione di elementi figlio

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 personalizzati per ottenere un comportamento di layout specifico personalizzato.

Durante il passaggio di misurazione, viene valutato ogni membro dell'insieme Children. Il processo ha inizio con una chiamata al metodo Measure. Questo metodo viene chiamato all'interno dell'implementazione dell'elemento Panel padre e non deve essere chiamato in modo esplicito per eseguire il layout.

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

In secondo luogo, vengono elaborate le proprietà di framework definite su FrameworkElement, che determina la modifica del valore di constraintSize. Queste proprietà descrivono in genere le caratteristiche di ridimensionamento dell'oggetto UIElement sottostante, ad esempio le relative proprietà Height, Width, Margin e Style. Ognuna di queste proprietà può modificare lo spazio necessario per la visualizzazione dell'elemento. Il metodo MeasureOverride viene quindi chiamato con constraintSize come parametro.

NotaNota

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 in base a un passaggio di rendering effettivo e potrebbe subire un rallentamento rispetto al valore impostato di proprietà quali Height che rappresentano 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 corrispondente, operazione che viene eseguita durante la chiamata al metodo MeasureCore. Il valore DesiredSize viene archiviato da Measure e verrà utilizzato durante il passaggio di disposizione del contenuto.

Il passaggio 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 e gli eventuali margini aggiuntivi che potrebbero modificare le dimensioni di rendering dell'elemento. Il metodo ArrangeCore genera un oggetto arrangeSize, che viene passato al metodo ArrangeOverride di Panel come parametro. ArrangeOverride genera l'oggetto finalSize dell'elemento figlio. Il metodo ArrangeCore esegue infine una valutazione delle proprietà di offset, ad esempio margine e allineamento, e posiziona l'elemento figlio all'interno del 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 padre, il processo di layout può essere considerato completato.

Elementi Panel e comportamenti di layout personalizzati

In WPF è incluso un gruppo di elementi che derivano da Panel. Questi elementi Panel consentono l'utilizzo di molti layout complessi. Tramite l'elemento StackPanel è ad esempio 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 seguente vengono riepilogati gli elementi Panel 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 l’insieme dei dati 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. L'ordinamento successivo procede in sequenza dall'alto verso il basso o da destra verso sinistra, a seconda del valore della proprietà Orientation.

Per le applicazioni che richiedono un layout 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 (la pagina potrebbe essere in inglese).

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 layout. Di conseguenza, è consigliabile evitare di attivare il sistema di layout quando non è necessario. Per ottenere prestazioni ottimali, tenere presenti le considerazioni seguenti.

  • Essere consapevoli delle modifiche a valori di proprietà che forzeranno un aggiornamento ricorsivo da parte del sistema di layout.

    Le proprietà di dipendenza i cui valori possono determinare l'inizializzazione del sistema di layout sono contrassegnate con flag pubblici. Le proprietà AffectsMeasure e AffectsArrange forniscono indicazioni utili relativamente alle modifiche a valori di proprietà che forzeranno un aggiornamento ricorsivo da parte del sistema di layout. In generale, le proprietà che possono influire sulle dimensioni del riquadro di un elemento dispongono di un flag AffectsMeasure impostato su true. Per ulteriori informazioni, vedere Cenni preliminari sulle proprietà di dipendenza.

  • Quando è possibile, utilizzare RenderTransform anziché LayoutTransform.

    Una proprietà LayoutTransform può rappresentare un modo molto utile di influire sul contenuto di un'user interface (UI). Se l'effetto della trasformazione non deve tuttavia avere alcun impatto sulla posizione di altri elementi, è consigliabile utilizzare la proprietà RenderTransform, in quanto RenderTransform non richiama il sistema di layout. LayoutTransform applica la trasformazione e forza un aggiornamento del layout ricorsivo in modo che venga considerata la nuova posizione dell'elemento interessato.

  • Evitare chiamate non necessarie al metodo UpdateLayout.

    Il metodo UpdateLayout forza un aggiornamento ricorsivo del layout che spesso non è necessario. A meno che non si sia certi della reale necessità di un aggiornamento completo, lasciare che questo metodo venga chiamato automaticamente dal sistema di layout.

  • Quando si utilizza un insieme Children di grandi dimensioni, considerare l'opportunità di utilizzare un oggetto VirtualizingStackPanel anziché un normale oggetto StackPanel.

    La virtualizzazione dell'insieme figlio, VirtualizingStackPanel determina il mantenimento in memoria solo degli oggetti che si trovano attualmente all'interno dell'oggetto Riquadro di visualizzazione dell'elemento padre. Di conseguenza, nella maggior parte degli scenari le prestazioni risultano notevolmente migliorate.

Rendering dei subpixel e arrotondamento del layout

Il sistema grafico WPF utilizza unità indipendenti dal dispositivo per consentire l'indipendenza dalla risoluzione e dal dispositivo. Ogni pixel indipendente dal dispositivo viene ridimensionato automaticamente in base all'impostazione dei dots per inch (dpi) del sistema. In questo modo, le applicazioni WPF vengono ridimensionate correttamente per diverse impostazioni dpi e l'applicazione rileva automaticamente i dpi.

Questa indipendenza dai dpi può tuttavia determinare un rendering irregolare dei bordi a causa dell'anti-aliasing. Questo fenomeno, che si presenta in genere sotto forma di bordi sfuocati o semitrasparenti, può verificarsi quando un bordo si trova in corrispondenza del centro di un pixel, anziché tra i pixel del dispositivo. Il sistema di layout consente di regolare questo fenomeno con l'arrotondamento del layout. L'arrotondamento del layout consiste nell'arrotondamento da parte del sistema di layout di qualsiasi valore di pixel non integrale durante il passaggio di layout.

L'arrotondamento del layout è disabilitato per impostazione predefinita. Per abilitare l'arrotondamento del layout, impostare la proprietà UseLayoutRounding su true su qualsiasi oggetto 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 per l'intera interfaccia utente, impostare UseLayoutRounding su true sul contenitore radice. Per un esempio, vedere UseLayoutRounding.

Fasi successive

La comprensione dei processi di misurazione e disposizione degli elementi illustrati nel primo passaggio è fondamentale per la comprensione del layout. Per ulteriori informazioni sugli 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 (la pagina potrebbe essere in inglese). Per mettere in pratica tutte queste nozioni realizzando una semplice applicazione, vedere Procedura dettagliata: introduzione a WPF.

Vedere anche

Riferimenti

FrameworkElement

UIElement

Concetti

Cenni preliminari sugli elementi Panel

Panoramica su allineamento, margini e spaziatura interna

Ottimizzazione delle prestazioni: layout e progettazione