Casino online











Mercato forex






C11. La grafica


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 è instanaziabile. 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.
Tutto quello che si deve fare, in conclusione, è usufruire della proprietà Graphics esposta dal parametro "e" nell'evento Paint, comune a tutti i derivati di Control.
Ecco ora un elenco dei membri più importanti di Graphics:
  • Clear(C) : cancella tutto il contenuto di Graphics, nei suoi margini, e lo rimpie con un colore uniforme definito da C
  • CompositingMode : determina il modo in cui due o più immagini sovrapposte vengano disegnate. È determinato da un enumeratore che assume solamente due valori: SourceCopy (la parte più recente rimpiazza quella esistente, "sovrascrivendo" i propri colori sui vecchi) e SourceOver (le due parti vengono sovrapposte in modo da formare una sfumatura, in cui entra prepotentemente in gioco il fattore Alpha, ossia la trasparenza, che determina quale dei due colori prevalga sull'altro e come debbano essere miscelati)
  • CompositingQuality : determina la qualità dell'operazione di composizione sopra illustrata. I valori dell'enumeratore sono pochi, e permettono di scegliere se ottenere una maggior qualità o una maggior velocità oppure se considerare il fattore di correzione gamma
  • CopyFromScreen(P1, P2, Size) : questa procedura è davvero molto utile. Permette di catturare una parte dello schermo e riprodurla sul supporto dell'oggetto Graphics (che, in definitiva, è la superificie del controllo a cui esso appartiene). Accetta tre argomenti: il primo, P1 As Point, determina il margine superiore sinistro della regione dello schermo da cui prelevare l'immagine; il secondo, P2 As Point, determina il margine superiore sinistro della regione di Graphics su cui copiare l'immagine; l'ultimo è di tipo Size e specifica larghezza e altezza della regione da prelevare. Ad esempio, si supponga che questo codice sia eseguito nell'evento Paint del form:
    e.Graphics.CopyFromScreen(New Point(0, 0), New Point(50, 50), _ 
        New Size(200, 200)) 
    Ebbene, il quadrato di lato 200 pixel che inizia nel punto (0,0) dello schermo (ossia in alto a sinistra), verrà copiato nella superficie del form a partire dal punto (50,50)
  • DpiX, DpiY : restituiscono rispettivamente la risoluzione su X e su Y, in punti per pixel
  • Draw... : tutte le procedure che iniziano per "Draw" permettono di disegnare l'elemento corrispondente sul supporto di Graphics. A seconda dell'entità geometrica, cambiano i parametri, che sono sempre visibili grazie al fumetto che li suggerisce
  • Fill... : tutte le procedura che iniziano per "Fill" disegnano una figura e la riempiono con lo stesso colore
  • FromHdc(Ptr) : inizializza e restituisce un oggetto Graphics partendo da un'immagine: di tale immagine si dispone solo di un puntatore intero (System.IntPtr). Può essere utilizzata in rari casi, ad esempio nel Marshalling di oggetti
  • FromHwnd(Ptr) : inizializza e restituisce un oggetto Graphics partendo da una finestra: di tale finestra si dispone solo dell'handle
  • FromImage(Img) : inizializza e restituisce un oggetto Graphics partendo da un'immagine Img As Image
  • RenderingOrigin : specifica quale sia l'origine del rendering, ossia il punto considerato (0,0)
  • ResetTransorm : annulla ogni trasformazione
  • RotateTransform(A) : effettua una rotazione di A gradi su tutti gli elementi di Graphics
  • ScalteTransform(sX, sY) : effettua una trasformazione delle dimensioni, mltiplicandole per sX su X e per sY su Y
  • SmoothingMode : determina quale modalità usare per smussare linee e percorsi curvi. Per un buon risultato si può usare l'anti-alias, che riduce di molto le sgranature evidenti prodotte dai pixel
  • Transformation : restituisce o imposta una matrice che rappresenta tutti i cambiamenti dello spazio 2D
  • TranslateTransform(dX, dY) : trasla tutto il contenuto di Graphics di un vettore (dX, dY)
Nell'esempio che segue, scriverò un programma per disegnare grafici a torta bidimensionali.
Il tutto si divide in tre diversi sorgenti: una libreria di classi GraphItems, un controllo utente GraphArea e l'applicazione principale.


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.
La seconda classe esposta rappresenta uno spicchio del grafico a torta e contiene le informazioni e la procedura per poterlo disegnare. La terza, invece, rappresenta l'etichetta corrispondente al colore nella legenda: il risultato che visualizzerà sullo schermo è un quadratino colorato al cui fianco presenzia la didascalia associata al colore e il suo valore. Ecco il codice:
'Questa classe astratta costituisce la base per ogni
'elemento che andrà ad essere disegnato sul controllo
Public MustInherit Class GraphItem
    'I colori possono essere sotto forma di Pen o Brush
    'quindi metto due proprietà
    Private _ColorPen As Pen
    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

    'Queste proprietà sono inutili: è possibile
    'inizializzare un nuovo oggetto Pen o SolidBrush (derivato
    'di Brush) con il costruttore che accetta un argomento di
    'tipo Color. Tuttavia per ora lascio così
    Public Shared ReadOnly Property CommonPens(ByVal Index As Byte) As Pen
        Get
            Select Case Index
                Case 0
                    Return Pens.Red
                Case 1
                    Return Pens.Orange
                Case 2
                    Return Pens.Yellow
                Case 3
                    Return Pens.Green
                Case 4
                    Return Pens.LightBlue
                Case 5
                    Return Pens.Blue
                Case 6
                    Return Pens.Violet
                Case 7
                    Return Pens.Black
                Case Else
                    Return Pens.White
            End Select
        End Get
    End Property

    Public Shared ReadOnly Property CommonBrushes(ByVal Index As Byte) As Brush
        Get
            Select Case Index
                Case 0
                    Return Brushes.Red
                Case 1
                    Return Brushes.Orange
                Case 2
                    Return Brushes.Yellow
                Case 3
                    Return Brushes.Green
                Case 4
                    Return Brushes.LightBlue
                Case 5
                    Return Brushes.Blue
                Case 6
                    Return Brushes.Violet
                Case 7
                    Return Brushes.Black
                Case Else
                    Return Brushes.White
            End Select
        End Get
    End Property

    'Ogni elemento deve esporre la procedura Draw,
    'per mezzo della quale il controllo GraphArea lo disegnerà
    'sulla propria superfice. G sarà l'oggetto
    'Graphics associato al controllo.
    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
        G.FillPie(Me.ColorBrush, New Rectangle(UpperLeft, Size), _ 
            Me.StartAngle, Me.SweepAngle)
        'Quindi disegna il contorno del pezzo in nero
        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 


GraphArea
Il controllo utente non è altro che una classe al cui interno coesistono controlli esistenti strettamente legati tra di loro da un unico codice. Assomiglia un pò alla creazione di un form. Per aggiungere un nuovo controllo utente, cliccare col destro, nel solution explorer, sul nome del progetto, quindi selezionare Add Item e poi User Control. Arrivati qui, basta impostare il colore di sfondo (BackColor) su bianco. Il codice è molto semplice, poichè il controllo in questo caso ha la sola funzione di fungere da "tela" su cui disegnare. Ecco il codice:
Public Class DrawArea
    'Collezione degli elementi da disegnare
    Private _Items As New List(Of GraphItem)

    Public ReadOnly Property Items() As List(Of GraphItem)
        Get
            Return _Items
        End Get
    End Property

    Private Sub DrawArea_Paint(ByVal sender As Object, _
        ByVal e As PaintEventArgs) Handles MyBase.Paint
        'Attiviamo l'anti-alias
        'Così il disegno diventa più fluido
        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
        For Each Item As GraphItem In Me.Items
            Item.Draw(e.Graphics)
        Next
    End Sub
End Class 


L'applicazione principale
Prima di proseguire, bisogna far sì che DrawArea entri nella toolbox insieme agli altri controlli: cliccare su Build->Build [Nome progetto]. Ora DrawArea apparià nella toolbox ai primissimi posti, con l'icona di un ingranaggio viola:


Nuovo controllo DrawArea

Si aggiunga, quindi, un DrawArea con il nome di daGraph, una DataGridView di nome dgvValues e un pulsante di nome cmdDraw e testo "Disegna". Una volta posizionati i vari componenti, bisogna scrivere il codice:
Class Form1
    Private Sub cmdDraw_Click(ByVal sender As Object, ByVal e As EventArgs) _ 
        Handles cmdDraw.Click
        Dim Total As Single

        daGraph.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
        'Colore della riga
        Dim Color As Byte
        '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"
                    Color = 0
                Case "Arancio"
                    Color = 1
                Case "Giallo"
                    Color = 2
                Case "Verde"
                    Color = 3
                Case "Azzurro"
                    Color = 4
                Case "Indaco"
                    Color = 5
                Case "Viola"
                    Color = 6
                Case "Nero"
                    Color = 7
            End Select

            'Inizializza la nuova etichetta
            Lab = New ColorLabel(Text)
            Lab.ColorPen = GraphItem.CommonPens(Color)
            Lab.ColorBrush = GraphItem.CommonBrushes(Color)
            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.ColorPen = GraphItem.CommonPens(Color)
            Piece.ColorBrush = GraphItem.CommonBrushes(Color)

            'Tiene traccia dell'angolo
            PrevAngle += Value * 360 / Total
            'Si sposta più in giù per la prossima etichetta
            Y += 20
            'Aggiunge gli elementi
            daGraph.Items.Add(Lab)
            daGraph.Items.Add(Piece)
        Next

        'E aggiorna il rendering
        daGraph.Refresh()
    End Sub
End Class 
Ed ecco un esempio di come si presenterà alla fine, tutta l'applicazione:

Et voilà!





 

The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.