Layout

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 include le sezioni seguenti:

Rettangoli di selezione degli elementi

Quando si pensa al layout in WPF, è importante comprendere il rettangolo di selezione che circonda tutti gli elementi. Ogni FrameworkElement oggetto utilizzato dal sistema di layout può essere considerato come un rettangolo che viene inserito nel layout. La LayoutInformation classe 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 . L'elaborazione di questi dati, il sistema di layout è in grado di calcolare la posizione di tutti gli elementi figlio di un particolare Paneloggetto . È importante ricordare che le caratteristiche di ridimensionamento definite nell'elemento padre, ad esempio , Borderinfluiscono sui relativi elementi figlio.

La figura seguente mostra un layout semplice.

Screenshot that shows a typical grid, no bounding box superimposed.

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 TextBlock elemento è ospitato all'interno di un oggetto Grid. Mentre il testo riempie solo l'angolo superiore sinistro della prima colonna, lo spazio allocato per l'oggetto TextBlock è effettivamente molto più grande. Il rettangolo di selezione di qualsiasi FrameworkElement può essere recuperato utilizzando il GetLayoutSlot metodo . La figura seguente mostra il rettangolo di selezione per l'elemento TextBlock .

Screenshot that shows that the TextBlock bounding box is now visible.

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 a Grid, questa allocazione potrebbe ridurre o espandere, a seconda del tipo e delle dimensioni degli elementi aggiunti.

Lo slot di layout di TextBlock viene convertito in un Path oggetto utilizzando il GetLayoutSlot metodo . Questa tecnica può essere utile per la visualizzazione del rettangolo di selezione 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

Nella forma più semplice, il layout è un sistema ricorsivo che fa sì che un elemento venga ridimensionato, posizionato e disegnato. In particolare, il layout descrive il processo di misurazione e disposizione dei membri della raccolta di Children un Panel elemento. Il layout è un processo intensivo. Maggiore è la Children raccolta, maggiore è il numero di calcoli che devono essere eseguiti. La complessità può essere introdotta anche in base al comportamento di layout definito dall'elemento Panel proprietario della raccolta. Un oggetto relativamente semplice Panel, ad esempio Canvas, può avere prestazioni significativamente migliori rispetto a un oggetto più complesso Panel, ad esempio Grid.

Ogni volta che un figlio UIElement cambia posizione, può attivare un nuovo passaggio dal sistema di layout. Di conseguenza, è importante identificare gli eventi che possono richiamare il sistema di layout, perché una chiamata non necessaria può causare scarse prestazioni dell'applicazione. Di seguito viene descritto il processo avviato quando viene richiamato il sistema di layout.

  1. Un figlio UIElement inizia il processo di layout prima di tutto con le relative proprietà di base misurate.

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

  3. PanelViene applicata una logica specifica, ad esempio Dock direzione o stacking Orientation.

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

  5. La Children raccolta viene disegnata sullo schermo.

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

Questo processo e il modo in cui viene richiamato vengono descritti con maggiori dettagli nelle sezioni seguenti.

Misurazione e disposizione degli elementi figlio

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

Durante il passaggio della misura, ogni membro della Children raccolta viene valutato. Il processo inizia con una chiamata al Measure metodo . Questo metodo viene chiamato all'interno dell'implementazione dell'elemento padre Panel e non deve essere chiamato in modo esplicito per l'esecuzione del layout.

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

In secondo luogo, le proprietà del framework definite in FrameworkElement vengono elaborate, che influiscono sul valore di constraintSize. Queste proprietà descrivono in genere le caratteristiche di ridimensionamento dell'oggetto sottostante UIElement, ad esempio 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 ActualHeight proprietà è 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 effettivo, e può quindi essere leggermente indietro rispetto al valore impostato delle proprietà, ad esempio Height, che sono la base della modifica di 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 misurazione necessario per gli elementi figlio, i vincoli dell'elemento padre e così via.

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

Il passaggio arrange inizia con una chiamata al Arrange metodo . 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 per l'elaborazione ArrangeCore .

Il ArrangeCore metodo valuta l'oggetto dell'elemento DesiredSize figlio e valuta eventuali margini aggiuntivi che possono influire sulle dimensioni di cui è stato eseguito il rendering dell'elemento. ArrangeCore genera un arrangeSizeoggetto , che viene passato al ArrangeOverride metodo di Panel come parametro . ArrangeOverride genera l'oggetto finalSize dell'oggetto figlio. Infine, il ArrangeCore metodo esegue una valutazione finale delle proprietà di offset, ad esempio margine e allineamento, e inserisce l'elemento figlio all'interno dello slot di layout. L'elemento figlio non deve necessariamente (e in genere non lo fa) occupare l'intero spazio allocato. Il controllo viene quindi restituito all'elemento padre Panel e il processo di layout è completo.

Elementi Panel e comportamenti di layout personalizzati

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

La tabella seguente riepiloga gli elementi di layout Panel disponibili.

Nome elemento Panel Descrizione
Canvas Definisce un'area all'interno della quale è possibile posizionare in modo esplicito gli elementi figlio in base alle coordinate relative all'area Canvas .
DockPanel Definisce un'area all'interno della quale è possibile disporre elementi figlio orizzontalmente o verticalmente, l'uno rispetto all'altro.
Grid Definisce un'area griglia flessibile costituita da righe e colonne.
StackPanel Dispone gli elementi figlio su una sola riga che può essere orientata orizzontalmente o verticalmente.
VirtualizingPanel Fornisce un framework per Panel gli elementi che virtualizzano la raccolta di dati figlio. Questa è una classe abstract.
WrapPanel Posiziona gli elementi figlio in sequenza da sinistra a destra, interrompendo il contenuto al raggiungimento del bordo della casella contenitore e facendolo ripartire dalla riga successiva. L'ordinamento successivo viene eseguito in sequenza dall'alto verso il basso o da destra a sinistra, a seconda del valore della Orientation proprietà.

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

Considerazioni sulle prestazioni del layout

Il layout è un processo ricorsivo. Ogni elemento figlio in una Children raccolta viene elaborato durante ogni chiamata del sistema di layout. Di conseguenza, è consigliabile evitare l'attivazione del sistema di layout quando non è necessaria. Le considerazioni seguenti possono essere utili per ottenere prestazioni migliori.

  • Identificare le modifiche dei valori di proprietà che forzeranno un aggiornamento ricorsivo da parte del sistema di layout.

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

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

    Un può essere un modo molto utile per influire sul contenuto di un'interfaccia utente.A LayoutTransform can be a very useful way to affect the content of a user interface (UI). Tuttavia, se l'effetto della trasformazione non deve influire sulla posizione di altri elementi, è preferibile usare un RenderTransform oggetto , perché RenderTransform non richiama il sistema di layout. LayoutTransform applica la sua trasformazione e forza un aggiornamento ricorsivo del layout per tenere conto della nuova posizione dell'elemento interessato.

  • Evitare chiamate non necessarie a UpdateLayout.

    Il UpdateLayout metodo forza un aggiornamento ricorsivo del layout e spesso non è necessario. Se non si ha la certezza della necessità di un aggiornamento completo, lasciare che sia il sistema di layout a chiamare automaticamente questo metodo.

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

    Virtualizzando la raccolta figlio, l'unico VirtualizingStackPanel oggetto mantiene gli oggetti in memoria attualmente all'interno del ViewPort dell'elemento padre. Come conseguenza, le prestazioni risultano notevolmente migliorate nella maggior parte degli scenari.

Rendering dei sub-pixel 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 viene ridimensionato automaticamente con l'impostazione dei punti per pollice (dpi) del sistema. In questo modo le applicazioni WPF supportano correttamente il ridimensionamento per impostazioni dpi diverse e rende l'applicazione compatibile automaticamente con dpi.

Tuttavia, questa indipendenza dpi può creare rendering irregolare dei bordi a causa dell'anti-aliasing. Questi elementi, osservabili in genere come bordi sfocati o semitrasparenti, possono essere presenti quando la posizione dei bordi si trova al centro di un pixel del dispositivo invece che tra pixel del dispositivo. Il sistema di layout permette di ovviare a questo problema con l'arrotondamento del layout. L'arrotondamento del layout avviene quando il sistema di layout arrotonda valori di pixel non integrali durante il passaggio di layout.

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

E adesso

La capacità di identificare il modo in cui gli elementi vengono misurati e disposti è il primo passaggio per la comprensione del layout. Per altre informazioni sugli elementi disponibili Panel , vedere Panoramica dei pannelli. Per meglio determinare le diverse proprietà di posizionamento che possono influire sul layout, vedere Panoramica su allineamento, margini e spaziatura interna. Quando si è pronti per inserirli tutti insieme in un'applicazione leggera, vedere Procedura dettagliata: Prima applicazione desktop WPF.

Vedi anche