Guida al Visual Basic .NET
Capitolo 96° - La classe Graphics
La grafica è una delle parti meno usate, o meno comprese, del Framework .NET. Essenzialmente serve a disegnare tutto quello che
il supporto .NET di per sé non è progettato per fare. Ad esempio, si possono creare grafici, modificare immagini e riprodurre
effetti particolari. Tutta l'infrastruttura di controllo della grafica si basa su una classe portante, chiamata Graphics, che non possiede
alcun costruttore: per questo motivo non è istanaziabile. Dopo aver chiarito un concetto del genere, dovrebbe sorgere spontaneamente
il dubbio su come si possa fare, allora, per usarla, dato che non espone metodi statici e che non può essere inizializzata. La risposta
è semplice: ogni controllo possiede un proprio oggetto Graphics associato, per mezzo del quale viene disegnato sullo schermo e grazie
a cui il programmatore interviene nella sua visualizzazione. Questo fa pensare che in realtà il costruttore esista, ma sia specificato
come Private (o al massimo Friend) e perciò accessibile solo all'interno degli oscuri meccanismi .NET, i quali si occupano di fornirne uno a ogni controllo
durante la costruzione dell'interfaccia. Bisogna comunque ricordare che ci sono metodi statici factory per la crezione di Graphics a partire
da altre immagini o da altre finestre, ma nessuna fornisce un nuovo oggetto vuoto.
Nell'esempio che segue, scriverò un programma per disegnare grafici a torta bidimensionali. GraphItems
La libreria espone tre classi. La prima è GraphItem, una classe astratta che rappresenta la base per gli altri elementi. Si usa questo
tipo di tecnica poichè servirà immagazzinare diversi tipi di elementi in una sola lista: per evitare liste a tipizzazione
debole come ArrayList, si usa una lista a tipizzazione forte in cui il tipo generics collegato è costituito da una classe base
comune a tutte. Accade molto spesso di usare questa tecnica, perciò fate attenzione. 'Questa classe astratta costituisce la base per ogni 'elemento che andrà ad essere disegnato sul controllo Public MustInherit Class GraphItem 'Per disegnare delle forme geometriche con i metodi Draw 'si usano oggetti di tipo Pen (penna): una penna definisce 'il colore usato per tracciare le linee e il loro 'spessore. Sono presenti delle penne predefinite nella 'classe statica Pens: una per ogni colore (per tutte, 'l'ampiezza del tratto è costante e pari a 1). 'Noi useremo sempre delle penne nere per il contorno 'dei prossimi oggetti, ma ho voluto aggiungere 'questo membro per completezza. Private _ColorPen As Pen 'Allo stesso modo, per riempire delle forme deometriche 'coi metodi Fill, si usano i pennelli (Brush). Brush è 'una classe astratta che costituisce la base di tutti 'i pennelli derivati. Noi useremo dei SolidBrush, oggetti 'che colorano un'area con colore uniforme. Tuttavia, come 'spiegherò in seguito, esistono molti altri tipi 'di pennelli, ad esempio per eseguire sfumature o per 'riempire un'area con delle immagini Private _ColorBrush As Brush Public Property ColorPen() As Pen Get Return _ColorPen End Get Set(ByVal Value As Pen) _ColorPen = Value End Set End Property Public Property ColorBrush() As Brush Get Return _ColorBrush End Get Set(ByVal Value As Brush) _ColorBrush = Value End Set End Property 'Ogni elemento deve esporre la procedura Draw, 'per mezzo della quale esso disegnerà la propria 'rappresentazione sul supporto grafico specificato 'da G. Come già accennato, disegneremo tutto 'su un'immagine vuota creata da noi Public MustOverride Sub Draw(ByVal G As Graphics) End Class 'Un pezzo di torta XD Public Class PiePiece Inherits GraphItem 'I parametri necessari a disegnarla sono: il centro 'della torta, il raggio, l'ampiezza (in gradi) e 'l'angolo iniziale Private _Center As Point Private _Radius As Int32 Private _StartAngle, _EndAngle As Single 'Centro Public Property Center() As Point Get Return _Center End Get Set(ByVal Value As Point) _Center = Value End Set End Property 'Raggio Public Property Radius() As Int32 Get Return _Radius End Get Set(ByVal Value As Int32) _Radius = Value End Set End Property 'Angolo di partenza Public Property StartAngle() As Single Get Return _StartAngle End Get Set(ByVal Value As Single) _StartAngle = Value End Set End Property 'Ampiezza Public Property SweepAngle() As Single Get Return _EndAngle End Get Set(ByVal Value As Single) _EndAngle = Value End Set End Property Sub New(ByVal Center As Point, ByVal Radius As Int32, _ ByVal StartAngle As Single, ByVal SweepAngle As Single) Me.Center = Center Me.Radius = Radius Me.StartAngle = StartAngle Me.SweepAngle = SweepAngle End Sub Public Overrides Sub Draw(ByVal G As Graphics) 'Calcola il quadrato in cui è inscritta la circonferenza 'della quale lo spicchio fa parte Dim UpperLeft As New Point(Me.Center.X - Me.Radius, _ Me.Center.Y - Me.Radius) 'Calcola la dimensione di tale quadrato Dim Size As New Size(Me.Radius * 2, Me.Radius * 2) 'Riempie il pezzo di torta con il colore. FillPie 'riempie col pennello specificato un settore circolare 'dell'ellisse inscritto nel rettangolo passato come 'parametro, a partire dall'angolo StartAngle, 'spazzando un angolo SweepAngle G.FillPie(Me.ColorBrush, New Rectangle(UpperLeft, Size), _ Me.StartAngle, Me.SweepAngle) 'Quindi disegna il contorno del pezzo in nero. Gli 'argomenti sono gli stessi, ad eccezione della penna 'al posto del pennello. Pens.Black è una 'penna nera di tratto 1 G.DrawPie(Pens.Black, New Rectangle(UpperLeft, Size), _ Me.StartAngle, Me.SweepAngle) End Sub End Class 'Un'etichetta che visualizza il colore e il testo 'corrispondente Public Class ColorLabel Inherits GraphItem 'I parametri necessari a disegnarla sono: il testo, 'le coordinate e il colore, che viene definito 'nella classe base Private _Text As String Private _Location As Point 'Testo Public Property Text() As String Get Return _Text End Get Set(ByVal Value As String) _Text = Value End Set End Property 'Coordinate Public Property Location() As Point Get Return _Location End Get Set(ByVal Value As Point) _Location = Value End Set End Property Sub New(ByVal Text As String) Me.Text = Text End Sub Public Overrides Sub Draw(ByVal G As System.Drawing.Graphics) 'Disegna un quadratino colorato G.FillRectangle(Me.ColorBrush, New Rectangle(Me.Location, _ New Size(20, 10))) 'Disegna il contorno nero al quadratino G.DrawRectangle(Pens.Black, New Rectangle(Me.Location, _ New Size(20, 10))) 'Disegna il testo 'New Font... inizializza un nuovo font, ossia Microsoft 'Sans Serif di dimensione 12, senza stili aggiuntivi G.DrawString(Me.Text, New Font("Microsoft Sans Serif", 12, FontStyle.Regular), Brushes.Black, Me.Location.X + 30, Me.Location.Y - 5) End Sub End Class
L'applicazione principale
L'applicazione principale contiene due componenti: un DataGridView e una PictureBox. Per vedere come li ho impostati, guardate lo screenshot in fondo alla pagina. Class Form1 Private Items As New List(Of GraphItem) Private Sub cmdDraw_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles cmdDraw.Click Dim Total As Single Items.Clear() 'Calcola il totale For Each Row As DataGridViewRow In dgvValues.Rows 'Controlla che il valore sia diverso da NULL If Row.Cells Is Nothing Then Continue For End If 'Quindi somma il valore della cella al totale Total += Row.Cells(0).Value Next 'Costruisce gli spicchi 'Valore di una riga Dim Value As Single 'Variabile ausiliare del ciclo: tiene traccia dell'angolo a cui 'si è arrivati Dim PrevAngle As Single = 0 'Anche questa, come sopra: tiene traccia a quale altezza si 'è arrivati con la legenda Dim Y As Int32 = 20 'Testo della riga Dim Text As String 'Pennello > colore Dim Br As Brush 'Una etichetta della legenda Dim Lab As ColorLabel 'Un pezzo della torta Dim Piece As PiePiece For Each Row As DataGridViewRow In dgvValues.Rows 'Controlla che i valori esistano e che la cella non 'sia l'ultima (che è sempre vuota) If Row.Cells Is Nothing OrElse _ Row.Index = dgvValues.RowCount - 1 Then Continue For End If Value = Row.Cells(0).Value 'Costruisce il testo della legenda, formato da quello della 'riga, con la specificazione, tra parentesi, del valore 'corrispondente e della percentuale Text = String.Format("{0} ({1:N2} - {2:N2}%)", _ Row.Cells(1).Value, Value, Value * 100 / Total) 'Questo sempre per l'intelligenza di DataGridView, Select Case Row.Cells(2).Value Case "Rosso" Br = Brushes.Red Case "Arancio" Br = Brushes.Orange Case "Giallo" Br = Brushes.Yellow Case "Verde" Br = Brushes.Green Case "Azzurro" Br = Brushes.LightBlue Case "Indaco" Br = Brushes.Blue Case "Viola" Br = Brushes.Violet Case "Nero" Br = Brushes.Black End Select 'Inizializza la nuova etichetta Lab = New ColorLabel(Text) Lab.ColorBrush = Br Lab.Location = New Point(280, Y) 'E il nuovo pezzo di torta. Value * 360 / Totale è 'l'ampiezza dell'angolo, ottenuta con la proporzione: 'Value : Total = x : 360 Piece = New PiePiece(New Point(150, 125), 100, _ PrevAngle, Value * 360 / Total) Piece.ColorBrush = Br 'Tiene traccia dell'angolo PrevAngle += Value * 360 / Total 'Si sposta più in giù per la prossima etichetta Y += 20 'Aggiunge gli elementi Items.Add(Lab) Items.Add(Piece) Next 'Crea una nuova immagine vuota Dim Img As New Bitmap(imgPreview.Width, imgPreview.Height) 'Prende l'oggetto Graphics associato a quell'immagine Dim G As Graphics = Graphics.FromImage(Img) 'Attiva l'anti-alias G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias 'E disegna ogni elemento For Each Item As GraphItem In Me.Items Item.Draw(G) Next 'Ogni cosa disegnata mediante G verrà trasferita 'sull'immagine Img associata G.Flush() imgPreview.Image = Img End Sub End Class
Ed ecco un esempio di come si presenterà alla fine, tutta l'applicazione:
C#, TypeScript, java, php, EcmaScript (JavaScript), Spring, Hibernate, React, SASS/LESS, jade, python, scikit, node.js, redux, postgres, keras, kubernetes, docker, hexo, etc...
|