Cenni preliminari sul rendering della grafica WPF
In questo argomento vengono forniti cenni preliminari sul livello visivo di WPF. Viene analizzato il ruolo della classe Visual per il supporto del rendering nel modello WPF.
Nel presente argomento sono contenute le seguenti sezioni.
- Ruolo dell'oggetto visivo
- Utilizzo di oggetti visivi per la compilazione di controlli
- Struttura ad albero visuale
- Comportamento di rendering visivo
- Classe VisualTreeHelper
- Argomenti correlati
Ruolo dell'oggetto visivo
La classe Visual rappresenta l'astrazione base da cui deriva ogni oggetto FrameworkElement. Svolge anche la funzione di punto di ingresso per la scrittura di nuovi controlli in WPF e in molti casi può essere concepita come l'handle di finestra (HWND) nel modello di applicazione Win32.
Visual è un oggetto WPF principale, il cui ruolo primario è quello di fornire supporto per il rendering. I controlli di interfaccia utente, ad esempio Button e TextBox, derivano dalla classe Visual e la utilizzano per salvare in modo permanente i dati di rendering. L'oggetto Visual fornisce supporto per:
Visualizzazione dell'output: eseguire il rendering del contenuto del disegno serializzato e salvato in modo permanente di un elemento visivo.
Trasformazioni: esecuzione di trasformazioni su un elemento visivo.
Area di visualizzazione: fornire supporto dell'area di visualizzazione per un elemento visivo.
Hit test: stabilire se una coordinata o una geometria è contenuta entro i limiti di un elemento visivo.
Calcoli del riquadro delimitatore: definire il rettangolo di delimitazione di un elemento visivo.
Tuttavia, l'oggetto Visual non include il supporto per le funzionalità non relative al rendering, ad esempio:
Gestione degli eventi
Layout
Stili
Associazione dati
Globalizzazione
Visual è esposto come classe astratta pubblica da cui devono essere derivate le classi figlio. Nella figura riportata di seguito viene illustrata la gerarchia degli oggetti visivi esposti in WPF.
Gerarchia delle classi visive
Classe DrawingVisual
DrawingVisual è una classe di disegno semplificata utilizzata per il rendering di forme, immagini o testo. Questa classe è considerata semplice perché non fornisce la gestione del layout o degli eventi, migliorando in tal modo le prestazioni in fase di esecuzione. Per questo motivo, i disegni sono ideali per sfondi e ClipArt. È possibile utilizzare DrawingVisual per creare un oggetto visivo personalizzato. Per ulteriori informazioni, vedere Utilizzo degli oggetti DrawingVisual.
Classe Viewport3DVisual
Viewport3DVisual crea un ponte tra gli oggetti Visual 2D e gli oggetti Visual3D. La classe Visual3D è la classe base per tutti gli elementi visivi 3D. Viewport3DVisual richiede la definizione di un valore Camera e di un valore Viewport. La fotocamera consente di visualizzare la scena. Il riquadro di visualizzazione stabilisce il punto in cui la proiezione esegue il mapping sulla superficie 2D. Per ulteriori informazioni sulle caratteristiche 3D in WPF, vedere Cenni preliminari sulla grafica tridimensionale.
Classe ContainerVisual
La classe ContainerVisual viene utilizzata come contenitore per un insieme di oggetti Visual. La classe DrawingVisual deriva dalla classe ContainerVisual e le consente di contenere un insieme di oggetti visivi.
Contenuto del disegno negli oggetti visivi
Un oggetto Visual archivia i dati di rendering come elenco di istruzioni di grafica vettoriale. Ciascun elemento nell'elenco di istruzioni rappresenta un insieme di dati di grafica di basso livello e risorse associate in un formato serializzato. Esistono quattro tipi diversi di dati di rendering che possono ospitare contenuto di disegno.
Tipo di contenuto del disegno |
Descrizione |
---|---|
Grafica vettoriale |
Rappresenta i dati di grafica vettoriale e tutte le informazioni relative agli oggetti Brush e Pen associati. |
Immagine |
Rappresenta un'immagine in un'area definita da un oggetto Rect. |
Glifo |
Rappresenta un disegno che esegue il rendering di un oggetto GlyphRun, ovvero una sequenza di glifi di una risorsa di tipi di carattere specificata. Il testo viene rappresentato nel modo seguente. |
Video |
Rappresenta un disegno che esegue il rendering di contenuto video. |
DrawingContext consente di compilare Visual con contenuto visivo. Quando si utilizzano i comandi di disegno di un oggetto DrawingContext, si archivia un insieme di dati di rendering che verranno in seguito utilizzati dal sistema di grafica, ma non si eseguono disegni sullo schermo in tempo reale.
Quando si crea un controllo WPF, ad esempio Button, il controllo genera dati di rendering in modo implicito per il disegno stesso. Ad esempio, l'impostazione della proprietà Content dell'oggetto Button determina l'archiviazione di una rappresentazione di rendering di un glifo nel controllo.
Un oggetto Visual descrive il proprio contenuto come uno o più oggetti Drawing contenuti in un oggetto DrawingGroup. Un oggetto DrawingGroup descrive anche le maschere di opacità, le trasformazioni, gli effetti bitmap e altre operazioni applicate al relativo contenuto. Quando viene eseguito il rendering del contenuto, le operazioni DrawingGroup vengono applicate nel seguente ordine: OpacityMask, Opacity, BitmapEffect, ClipGeometry, GuidelineSet, quindi Transform.
Nella figura riportata di seguito viene illustrato l'ordine in cui vengono applicate le operazioni DrawingGroup durante la sequenza di rendering.
Ordine delle operazioni DrawingGroup
Per ulteriori informazioni, vedere Cenni preliminari sugli oggetti Drawing.
Contenuto del disegno a livello visivo
Benché non venga mai creata direttamente un'istanza della classe DrawingContext, è possibile acquisire un contesto di disegno tramite alcuni metodi, quali DrawingGroup.Open e DrawingVisual.RenderOpen. Nell'esempio riportato di seguito un oggetto DrawingContext viene recuperato da un oggetto DrawingVisual, quindi viene utilizzato per disegnare un rettangolo.
' Create a DrawingVisual that contains a rectangle.
Private Function CreateDrawingVisualRectangle() As DrawingVisual
Dim drawingVisual As New DrawingVisual()
' Retrieve the DrawingContext in order to create new drawing content.
Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
' Create a rectangle and draw it in the DrawingContext.
Dim rect As New Rect(New Point(160, 100), New Size(320, 80))
drawingContext.DrawRectangle(Brushes.LightBlue, CType(Nothing, Pen), rect)
' Persist the drawing content.
drawingContext.Close()
Return drawingVisual
End Function
// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
Enumerazione del contenuto del disegno a livello visivo
Oltre ad altri utilizzi, gli oggetti Drawing forniscono un modello a oggetti per l'enumerazione del contenuto di Visual.
Nota |
---|
Quando viene enumerato il contenuto dell'oggetto visivo, vengono recuperati oggetti Drawing e non la rappresentazione sottostante dei dati di rendering come elenco di istruzioni della grafica vettoriale. |
Nell'esempio riportato di seguito viene utilizzato il metodo GetDrawing per recuperare il valore DrawingGroup di un oggetto Visual e per enumerarlo.
public void RetrieveDrawing(Visual v)
{
DrawingGroup dGroup = VisualTreeHelper.GetDrawing(v);
EnumDrawingGroup(dGroup);
}
// Enumerate the drawings in the DrawingGroup.
public void EnumDrawingGroup(DrawingGroup drawingGroup)
{
DrawingCollection dc = drawingGroup.Children;
// Enumerate the drawings in the DrawingCollection.
foreach (Drawing drawing in dc)
{
// If the drawing is a DrawingGroup, call the function recursively.
if (drawing.GetType() == typeof(DrawingGroup))
{
EnumDrawingGroup((DrawingGroup)drawing);
}
else if (drawing.GetType() == typeof(GeometryDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(ImageDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(GlyphRunDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(VideoDrawing))
{
// Perform action based on drawing type.
}
}
}
Utilizzo di oggetti visivi per la compilazione di controlli
Poiché molti oggetti disponibili in WPF sono composti da altri oggetti visivi, possono contenere gerarchie variabili di oggetti discendenti. Molti elementi dell'interfaccia utente disponibili in WPF, come i controlli, sono composti da più oggetti visivi, che rappresentano tipi diversi di elementi di rendering. Ad esempio, il controllo Button può contenere numerosi altri oggetti, tra cui ClassicBorderDecorator, ContentPresenter e TextBlock.
Nel codice riportato di seguito viene illustrato un controllo Button definito nel markup.
<Button Click="OnClick">OK</Button>
Se si esegue l'enumerazione degli oggetti visivi che compongono il controllo Button predefinito, si ottiene la gerarchia di oggetti visivi illustrata di seguito:
Diagramma della gerarchia della struttura ad albero visuale
Il controllo Button contiene un elemento ClassicBorderDecorator, che a sua volta contiene un elemento ContentPresenter. L'elemento ClassicBorderDecorator è responsabile del disegno di un bordo e di uno sfondo per Button. L'elemento ContentPresenter è responsabile della visualizzazione del contenuto di Button. In questo caso, poiché viene visualizzato testo, l'elemento ContentPresenter contiene un elemento TextBlock. Il fatto che il controllo Button utilizzi un oggetto ContentPresenter implica che sia possibile rappresentare il contenuto da altri elementi, ad esempio un oggetto Image o una geometria, ad esempio un oggetto EllipseGeometry.
Modelli di controllo
La chiave per l'espansione di un controllo in una gerarchia di controlli è ControlTemplate. Un modello di controllo specifica la gerarchia visiva predefinita per un controllo. Quando si fa riferimento in modo esplicito a un controllo, si fa riferimento in modo implicito alla relativa gerarchia visiva. È possibile eseguire l'override dei valori predefiniti per un modello di controllo per dare un aspetto visivo personalizzato a un controllo. Ad esempio, è possibile modificare il valore del colore di sfondo del controllo Button in modo che utilizzi un valore di colore sfumato lineare anziché un valore di colore a tinta unita. Per ulteriori informazioni, vedere Stili e modelli di Button.
Un elemento dell'interfaccia utente, ad esempio un controllo Button, contiene numerosi elenchi di istruzioni della grafica vettoriale in cui viene descritta l'intera definizione di rendering di un controllo. Nel codice riportato di seguito viene illustrato un controllo Button definito nel markup.
<Button Click="OnClick">
<Image Source="images\greenlight.jpg"></Image>
</Button>
Se si eseguisse l'enumerazione degli oggetti visivi e degli elenchi di istruzioni della grafica vettoriale che compongono il controllo Button, si otterrebbe la gerarchia di oggetti illustrata di seguito:
Diagramma della struttura ad albero visuale e dei dati di rendering
Il controllo Button contiene un elemento ClassicBorderDecorator, che a sua volta contiene un elemento ContentPresenter. L'elemento ClassicBorderDecorator è responsabile del disegno di tutti gli elementi grafici discreti che compongono il bordo e lo sfondo di un pulsante. L'elemento ContentPresenter è responsabile della visualizzazione del contenuto di Button. In questo caso, poiché viene visualizzata un'immagine, l'elemento ContentPresentercontiene un elemento Image.
È opportuno tenere in considerazione alcuni punti sulla gerarchia degli oggetti visivi e sugli elenchi di istruzioni della grafica vettoriale:
L'ordinamento nella gerarchia rappresenta l'ordine di rendering delle informazioni di disegno. Gli elementi figlio vengono attraversati da sinistra a destra, dall'alto in basso, a partire dall'elemento visivo radice. Se un elemento dispone di elementi figlio visivi, essi vengono attraversati prima degli elementi di pari livello.
Gli elementi dei nodi non foglia nella gerarchia, ad esempio ContentPresenter, vengono utilizzati per contenere elementi figlio e non contengono elenchi di istruzioni.
Se un elemento visivo contiene sia un elenco di istruzioni della grafica vettoriale sia un elemento visivo figlio, l'elenco di istruzioni nell'elemento visivo padre viene sottoposto a rendering prima dell'esecuzione di disegni in qualsiasi oggetto visivo figlio.
Il rendering degli elementi nell'elenco di istruzioni della grafica vettoriale viene eseguito da sinistra a destra.
Struttura ad albero visuale
La struttura ad albero visuale contiene tutti gli elementi visivi utilizzati nell'interfaccia utente di un'applicazione. Poiché un elemento visivo contiene informazioni di disegno salvate in modo permanente, è possibile considerare la struttura ad albero visuale come un grafico di scena, contenente tutte le informazioni di rendering necessarie per comporre l'output per il dispositivo di visualizzazione. Questa struttura ad albero è la somma di tutti gli elementi visivi creati direttamente dall'applicazione, nel codice o nel markup. La struttura ad albero visuale contiene inoltre tutti gli elementi visivi creati dall'espansione del modello di elementi, quali controlli e oggetti dati.
Nel codice riportato di seguito viene illustrato un elemento StackPanel definito nel markup.
<StackPanel>
<Label>User name:</Label>
<TextBox />
<Button Click="OnClick">OK</Button>
</StackPanel>
Se si eseguisse l'enumerazione degli oggetti visivi che compongono l'elemento StackPanel dell'esempio di markup, si otterrebbe la gerarchia di oggetti visivi illustrata di seguito:
Diagramma della gerarchia della struttura ad albero visuale
Ordine di rendering
La struttura ad albero visuale determina l'ordine di rendering degli oggetti visivi e di disegno di WPF. L'ordine di scorrimento inizia con l'elemento visivo radice, ovvero il nodo di primo piano della struttura ad albero visuale. Vengono quindi attraversati gli elementi figlio dell'elemento visivo radice, da sinistra a destra. Se un elemento visivo ha elementi figlio, questi ultimi vengono attraversati prima degli elementi visivi di pari livello. Ciò significa che il contenuto di un elemento visivo figlio viene sottoposto a rendering prima del contenuto dell'elemento visivo stesso.
Diagramma dell'ordine di rendering della struttura ad albero visuale
Elemento visivo radice
L'elemento visivo radice è l'elemento di primo piano nella gerarchia di una struttura ad albero visuale. Nella maggior parte delle applicazioni, la classe base dell'elemento visivo radice è Window oppure NavigationWindow. Tuttavia, se in un'applicazione Win32 venissero ospitati oggetti visivi, l'elemento visivo radice sarebbe l'elemento visivo di primo piano ospitato nella finestra Win32. Per ulteriori informazioni, vedere Esercitazione: hosting di oggetti visivi in un'applicazione Win32.
Relazione con l'albero logico
L'albero logico in WPF rappresenta gli elementi di un'applicazione in fase di esecuzione. Sebbene questa struttura ad albero non venga modificata direttamente, questa visualizzazione dell'applicazione è utile per comprendere l'ereditarietà delle proprietà e il routing degli eventi. A differenza della struttura ad albero visuale, l'albero logico può rappresentare oggetti dati non visivi, ad esempio ListItem. In molti casi, il mapping dell'albero logico viene eseguito in modo molto simile alle definizioni di markup di un'applicazione. Nel codice riportato di seguito viene illustrato un elemento DockPanel definito nel markup.
<DockPanel>
<ListBox>
<ListBoxItem>Dog</ListBoxItem>
<ListBoxItem>Cat</ListBoxItem>
<ListBoxItem>Fish</ListBoxItem>
</ListBox>
<Button Click="OnClick">OK</Button>
</DockPanel>
Se si eseguisse l'enumerazione degli oggetti logici che compongono l'elemento DockPanel dell'esempio di markup, si otterrebbe la gerarchia di oggetti logici illustrata di seguito:
Diagramma dell'albero logico
Sia la struttura ad albero visuale sia l'albero logico sono sincronizzati all'insieme corrente di elementi applicazione e riflettono qualsiasi operazione di aggiunta, cancellazione o modifica di elementi. Tuttavia, presentano visualizzazioni diverse dell'applicazione. A differenza della struttura ad albero visuale, l'albero logico non espande un elemento ContentPresenter del controllo. Ciò significa che non esiste una corrispondenza diretta uno-a-uno tra un albero logico e una struttura ad albero visuale per lo stesso insieme di oggetti. Infatti, richiamando il metodo GetChildren dell'oggetto LogicalTreeHelper e il metodo GetChild dell'oggetto VisualTreeHelper con lo stesso elemento come parametro vengono restituiti risultati diversi.
Per ulteriori informazioni sull'albero logico, vedere Strutture ad albero in WPF.
Visualizzazione della struttura ad albero visuale con XamlPad
Lo strumento XamlPad di WPF offre un'opzione per la visualizzazione e l'esplorazione della struttura ad albero visuale che corrisponde al contenuto di XAML attualmente definito. Fare clic sul pulsante Show Visual Tree sulla barra dei menu per visualizzare la struttura ad albero visuale. Nella figura seguente viene illustrata l'espansione del contenuto XAML in nodi della struttura ad albero visuale nel pannello Visual Tree Explorer di XamlPad:
Pannello Visual Tree Explorer in XamlPad
Si noti come in ognuno dei controlli Label, TextBox e Button venga visualizzata una gerarchia di oggetti visivi separata nel pannello Visual Tree Explorer di XamlPad. Questa situazione si verifica poiché i controlli WPF dispongono di un oggetto ControlTemplate che contiene la struttura ad albero visuale di tale controllo. Quando si fa riferimento in modo esplicito a un controllo, si fa riferimento in modo implicito alla relativa gerarchia visiva.
Analisi delle prestazioni visive
WPF fornisce una suite di strumenti di analisi delle prestazioni che consentono di esaminare il comportamento dell'applicazione in fase di esecuzione e di determinare quale tipo di ottimizzazione delle prestazioni è possibile applicare. Lo strumento Visual Profiler consente di ottenere una rappresentazione grafica completa dei dati relativi alle prestazioni eseguendo direttamente il mapping alla struttura ad albero visuale dell'applicazione. In questa schermata, la sezione CPU Usage di Visual Profiler fornisce un'indicazione dettagliata dell'utilizzo dei servizi WPF da parte di un oggetto, ad esempio il rendering e il layout.
Output visualizzato di Visual Profiler
Comportamento di rendering visivo
WPF offre numerose funzionalità che hanno effetto sul comportamento di rendering degli oggetti visivi: grafica in modalità differita, grafica vettoriale e grafica indipendente dal dispositivo.
Grafica in modalità differita
Uno dei fattori chiave per la comprensione del ruolo degli oggetti visivi è la differenza tra i sistemi di grafica in modalità immediata e in modalità differita. Un'applicazione Win32 standard basata su GDI o GDI+ utilizza un sistema di grafica in modalità immediata. Ciò significa che è compito dell'applicazione ridisegnare la porzione dell'area client invalidata, a causa di un'azione come il ridimensionamento di una finestra o la modifica dell'aspetto visivo di un oggetto.
Diagramma della sequenza di rendering di Win32
WPF utilizza invece un sistema in modalità differita. Ciò significa che gli oggetti applicazione che presentano un aspetto visivo definiscono un insieme di dati di disegno serializzato. Dopo aver definito i dati di disegno, il sistema è responsabile della successiva risposta a tutte le richieste di ridisegno per il rendering degli oggetti applicazione. Anche in fase di esecuzione è possibile modificare o creare oggetti applicazione e affidarsi al sistema per rispondere alle richieste di disegno. Il punto di forza di un sistema di grafica in modalità differita è dato dal fatto che le informazioni di disegno vengono sempre salvate in modo permanente in uno stato serializzato dall'applicazione, ma la responsabilità del rendering viene lasciata al sistema. Nel diagramma riportato di seguito viene illustrato come l'applicazione si basi su WPF per la risposta alle richieste di disegno.
Diagramma della sequenza di rendering WPF
Ridisegno intelligente
Uno dei vantaggi maggiori dell'utilizzo della grafica in modalità differita consiste nella possibilità offerta da WPF di ottimizzare in modo efficace gli elementi da ridisegnare nell'applicazione. Anche se si dispone di una scena complessa con livelli di opacità variabili, non è in genere necessario scrivere codice specifico per ottimizzare il ridisegno. Confrontare questa caratteristica con la programmazione Win32 in cui sono richieste numerose operazioni per ottimizzare l'applicazione riducendo al minimo la quantità di ridisegno nell'area di aggiornamento. Per un esempio della complessità dell'ottimizzazione del ridisegno in applicazioni Win32, vedere Ridisegno nell'area di aggiornamento.
Grafica vettoriale
In WPF viene utilizzata la grafica vettoriale come formato per i dati di rendering. La grafica vettoriale, che include Scalable Vector Graphics (SVG), metafile Windows (.wmf) e tipi di carattere TrueType, archivia i dati di rendering e li trasmette come elenco di istruzioni che descrive come ricreare un'immagine mediante primitive di grafica. Ad esempio, i tipi di carattere TrueType sono caratteri in formato vettoriale che descrivono un insieme di righe, curve e comandi, anziché una matrice di pixel. Uno dei vantaggi principali della grafica vettoriale è la possibilità di adattarsi a qualsiasi dimensione e risoluzione.
Diversamente dalla grafica vettoriale, la grafica bitmap archivia i dati di rendering come rappresentazione pixel per pixel di un'immagine, di cui è stato precedentemente eseguito il rendering per una risoluzione specifica. Una delle differenze principali tra i formati di grafica bitmap e vettoriale è la fedeltà all'immagine originale. Ad esempio, quando la dimensione di un'immagine di origine viene modificata, i sistemi di grafica bitmap allungano l'immagine, mentre i sistemi di grafica vettoriale la ridimensionano, conservando la fedeltà all'originale.
Nella figura seguente viene illustrata un'immagine di origine che è stata ridimensionata del 300%. Si notino le distorsioni visualizzate quando l'immagine di origine viene allungata come immagine di grafica bitmap, anziché ridimensionata come immagine di grafica vettoriale.
Differenze tra grafica raster e vettoriale
Nel markup riportato di seguito vengono illustrati due elementi Path definiti. Nel secondo elemento viene utilizzato un oggetto ScaleTransform per ridimensionare le istruzioni di disegno del primo elemento del 300%. Si noti che le istruzioni di disegno negli elementi Path rimangono invariate.
<Path
Data="M10,100 C 60,0 100,200 150,100 z"
Fill="{StaticResource linearGradientBackground}"
Stroke="Black"
StrokeThickness="2" />
<Path
Data="M10,100 C 60,0 100,200 150,100 z"
Fill="{StaticResource linearGradientBackground}"
Stroke="Black"
StrokeThickness="2" >
<Path.RenderTransform>
<ScaleTransform ScaleX="3.0" ScaleY="3.0" />
</Path.RenderTransform>
</Path>
Informazioni sulla grafica indipendente dalla risoluzione e dal dispositivo
Esistono due fattori di sistema che determinano la dimensione del testo e della grafica sullo schermo, risoluzione e DPI. La risoluzione indica il numero di pixel visualizzati sullo schermo. Con l'aumento della risoluzione i pixel vengono rimpiccioliti, con la conseguente riduzione della dimensione di testo e grafica. Un elemento grafico visualizzato su un monitor con impostazione 1024 x 768 avrà dimensioni inferiori con una risoluzione di 1600 x 1200.
L'altra impostazione di sistema, DPI, indica la dimensione di un pollice di schermo in pixel. La maggior parte dei sistemi Windows hanno un valore DPI pari a 96, che indica un pollice di schermo pari a 96 pixel. Con l'aumento dell'impostazione DPI le dimensioni del pollice di schermo aumentano, mentre con la riduzione del valore DPI le dimensioni del pollice di schermo diminuiscono. Ciò significa che, nella maggior parte dei sistemi, un pollice di schermo non ha le stesse dimensioni di un pollice reale. Quando si aumenta il valore DPI, le dimensioni della grafica e del testo compatibili con DPI aumentano poiché viene aumentata la dimensione del pollice di schermo. L'aumento del valore DPI può rendere più agevole la lettura del testo, specialmente a risoluzioni elevate.
Non tutte le applicazioni sono compatibili con DPI. Poiché alcune utilizzano i pixel hardware come unità di misura primaria, la modifica del valore DPI di sistema non ha effetto su tali applicazioni. Molte altre applicazioni utilizzano unità compatibili con DPI per indicare le dimensioni dei tipi di carattere, ma utilizzano i pixel per indicare ogni altro valore. L'eccessiva riduzione o l'eccessivo aumento del valore DPI può causare problemi di layout in tali applicazioni, poiché il testo delle applicazioni viene ridimensionato con l'impostazione DPI del sistema, a differenza dell'interfaccia utente. Questo problema è stato eliminato per le applicazioni sviluppate in WPF.
In WPF il ridimensionamento automatico è supportato tramite l'utilizzo del pixel indipendente dal dispositivo come unità di misura primaria, anziché i pixel hardware. La grafica e il testo vengono ridimensionati correttamente senza la necessità di operazioni aggiuntive da parte dello sviluppatore dell'applicazione. Nella figura riportata di seguito viene illustrato un esempio della modalità di visualizzazione di testo e grafica di WPF con impostazioni DPI diverse.
Grafica e testo con impostazioni DPI diverse
Classe VisualTreeHelper
La classe VisualTreeHelper è una classe di supporto statica che fornisce funzionalità di basso livello per la programmazione a livello degli oggetti visivi, utile in scenari molto specifici, come lo sviluppo di controlli personalizzati dalle prestazioni elevate. Nella maggior parte dei casi, gli oggetti del framework WPF di livello superiore, ad esempio Canvas e TextBlock, offrono caratteristiche di flessibilità e facilità di utilizzo superiori.
Hit testing
La classe VisualTreeHelper offre metodi per eseguire l'hit testing sugli oggetti visivi quando il supporto per l'hit testing predefinito non soddisfa esigenze specifiche. È possibile utilizzare i metodi HitTest della classe VisualTreeHelper per determinare se il valore di coordinata di un punto o di una geometria è compreso nei limiti di un determinato oggetto, ad esempio un controllo o un elemento grafico. Ad esempio, è possibile utilizzare l'hit testing per determinare se un clic del mouse all'interno del rettangolo di delimitazione di un oggetto viene eseguito nella geometria di un cerchio. È inoltre possibile scegliere di eseguire l'override dell'implementazione predefinita dell'hit testing per eseguire calcoli di hit testing personalizzati.
Per ulteriori informazioni sull'hit testing, vedere Hit testing a livello visivo.
Enumerazione della struttura ad albero visuale
La classe VisualTreeHelper offre funzionalità per l'enumerazione dei membri di una struttura ad albero visuale. Per recuperare un elemento padre, chiamare il metodo GetParent. Per recuperare un elemento figlio, o un discendente diretto, di un oggetto visivo, chiamare il metodo GetChild. Questo metodo restituisce un oggetto Visual figlio dell'elemento padre in corrispondenza dell'indice specificato.
Nell'esempio riportato di seguito viene illustrato come enumerare tutti i discendenti di un oggetto visivo. Si tratta di una tecnica che può risultare utile se si desidera serializzare tutte le informazioni di rendering relative a una gerarchia di oggetti visivi.
' Enumerate all the descendants of the visual object.
Public Shared Sub EnumVisual(ByVal myVisual As Visual)
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(myVisual) - 1
' Retrieve child visual at specified index value.
Dim childVisual As Visual = CType(VisualTreeHelper.GetChild(myVisual, i), Visual)
' Do processing of the child visual object.
' Enumerate children of the child visual object.
EnumVisual(childVisual)
Next i
End Sub
// Enumerate all the descendants of the visual object.
static public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
// Do processing of the child visual object.
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
Nella maggior parte dei casi, l'albero logico è una rappresentazione più utile degli elementi in un'applicazione WPF. Sebbene l'albero logico non venga modificato direttamente, questa visualizzazione dell'applicazione è utile per comprendere l'ereditarietà delle proprietà e il routing degli eventi. A differenza della struttura ad albero visuale, l'albero logico può rappresentare oggetti dati non visivi, ad esempio ListItem. Per ulteriori informazioni sull'albero logico, vedere Strutture ad albero in WPF.
La classe VisualTreeHelper offre metodi per la restituzione del rettangolo di delimitazione degli oggetti visivi. È possibile restituire il rettangolo di delimitazione di un oggetto visivo chiamando GetContentBounds. È possibile restituire il rettangolo di delimitazione di tutti gli oggetti discendenti di un oggetto visivo, compreso l'oggetto visivo stesso, chiamando GetDescendantBounds. Nel codice riportato di seguito viene illustrato come calcolare il rettangolo di delimitazione di un oggetto visivo e di tutti i relativi discendenti .
' Return the bounding rectangle of the parent visual object and all of its descendants.
Dim rectBounds As Rect = VisualTreeHelper.GetDescendantBounds(parentVisual)
// Return the bounding rectangle of the parent visual object and all of its descendants.
Rect rectBounds = VisualTreeHelper.GetDescendantBounds(parentVisual);
Vedere anche
Riferimenti
Concetti
Ottimizzazione delle prestazioni: grafica bidimensionale e creazione di immagini
Utilizzo degli oggetti DrawingVisual
Esercitazione: hosting di oggetti visivi in un'applicazione Win32