Appunti di Programmazione

Creative Commons License

Creiamo un grafico a barre

Lo scopo di questo tutorial è quello di mostrare come creare un semplice programma per la rappresentazione dei dati in un grafico a barre (istogrammi). Nell'esempio che segue vengono usati dei dati raccolti in un array, ciò non toglie che la fonte delle informazioni possa essere di altro genere (Dataset, DataGridView, TextBox...)

Avviamo un nuovo progetto Windows Application e impostiamo le seguenti proprietà del form:

Size:500;400
Text:Grafico a Barre

Inseriamo anche una PictureBox:

Anchor:Top, Bottom, Left, Right
Dock:Fill
BackColor:White

Dichiariamo un array, DatiGrafico, in cui inseriamo 12 valori single, e una variabile integer, NumeroValori, che contiene il numero di elementi dell'array.

Public Class Form1

    Dim datiGrafico() As Single = {120, 98, 69, 78, 236, 159, 64, 58, 169, 74, 211, 200}
    Dim numeroValori As Integer = 12

Adesso inseriamo una funzione per determinare il valore massimo fra quelli inseriti. Tale valore è utile per determinare la corretta scala del disegno in relazione alle dimensioni della form.

    Private Function CalcolaMassimo(ByVal valore() As Single) As Single

        Dim risultato As Single = 0

        For Each numero As Single In valore
        
            If numero > risultato Then risultato = numero
            
        Next

        Return risultato

    End Function

Adesso inseriamo il codice che disegna il grafico in base ai dati forniti:

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) _
    Handles PictureBox1.Paint

        Dim maxValore As Single = CalcolaMassimo(datiGrafico)

        If maxValore = 0 Then Exit Sub

la variabile maxValore contiene il valore calcolato dalla funzione descritta in precedenza.

La If...Then serve unicamente a impedire che venga disegnato un grafico nel caso in cui tutti i dati siano a zero. In questa situazione questa istruzione non ha alcun senso, ma può tornare utile nei casi in cui è necessario valutare il valore dei dati raccolti, e per evitare che venga disegnato un grafico in cui tutti i dati sono nulli.

Otteniamo l'oggetto Graphics attraverso il riferimento a PaintEventArgs. In questo caso non è necessario rilasciare le risorse usando il metodo Dispose.

        Dim gr As Graphics = e.Graphics

Questa istruzione condizionale fa si che il valore massimo ritornato dalla funzione descritta all'inizio, venga arrotondato per eccesso al multiplo di 10 più prossimo.
Nel nostro esempio il valore maggiore è 236, dopo la If...Then diventa 240.

        If (maxValore Mod 10) > 0 Then
                                    
            maxValore = (CType(maxValore, Long) \ 10) * 10 + 10
            
        End If

Vengono definiti la penna con cui disegnare gli assi, il font per scrivere i valori e il pennello con cui colorare le barre del grafico.

        Dim penBlack As New Pen(Color.Black, -1)
        Dim labelFont As New Font("Arial", 3, FontStyle.Regular)
        Dim ColoreTesto As New SolidBrush(Color.Blue)

Vengono definite le variabili che rappresentano gli angoli opposti dell'area grafica (x1;y1 e x2;y2), la dimensione delle scala lungo i due assi (scaleX e scaleY) e infine un rettangolo che rappresenta la barra da disegnare.

        Dim x1, y1 As Single
        Dim x2, y2 As Single
        Dim scaleX, scaleY As Single
        Dim oneBar As RectangleF

angolo in alto a sinistra.

        x1 = 10
        y1 = 10

angolo in basso a destra.

        x2 = numeroValori * 10 + 10
        y2 = maxValore + 10

definizione della scala lungo i due assi.

        scaleX = Me.PictureBox1.Width / (x2 + x1)
        scaleY = Me.PictureBox1.Height / (y2 + y1)

applicazione della scala lungo i due assi all'area grafica. scaleY risulta negativa per avere la direzione positiva dell'asse Y verso l'alto.

        gr.ScaleTransform(scaleX, -scaleY)

traslazione dell'origine del sistema di riferimento alle nuove coordinate (x1;-y2).

        gr.TranslateTransform(x1, -y2)

colora di bianco l'area grafica.

        gr.Clear(Color.White)

disegna il rettangolo che rappresenta la zona ove saranno disegnate le barre.

        gr.DrawRectangle(penBlack, New Rectangle(0, 0, 10 * numeroValori, CType(maxValore, Integer)))

disegna la griglia.

For xScan As Single = 10 To (numeroValori * 10 - 10) Step 10
                                    
            gr.DrawLine(penBlack, xScan, 0, xScan, maxValore)
            
        Next xScan

        For yScan As Single = 10 To maxValore Step 10
        
            gr.DrawLine(penBlack, 0, yScan, (numeroValori * 10), yScan)
            
        Next yScan

Disegna l'ombra delle barre del grafico.

        For xScan As Single = 0 To numeroValori - 1

            oneBar.X = CType(xScan * 10 + 0.6, Single)
            oneBar.Y = 0
            oneBar.Width = 6
            oneBar.Height = datiGrafico(CType(xScan, Integer))
            gr.FillRectangle(New SolidBrush(Color.FromArgb(127, Color.Gray)), oneBar)

Disegna la barra che rappresenta il valore nell'array nella posizione specificata.

            oneBar.X = xScan * 10 + 2
            oneBar.Y = 0
            oneBar.Height = datiGrafico(CType(xScan, Integer))
            gr.FillRectangle(New SolidBrush(Color.DarkOrange), oneBar)

        Next xScan

Cancella tutte le trasformazioni per disegnare i valori lungo gli assi, altrimenti sarebbero rappresentati capovolti a causa delle modifiche effettuate per avere l'origine in basso a sinistra e il verso positivo dell'asse Y rivolto in alto.

        gr.ResetTransform()

applica la scala all'area grafica.

        gr.ScaleTransform(scaleX, scaleY)

applica la traslazione dell'origine.

        gr.TranslateTransform(x1, y1)

disegna i valori di riferimento lungo gli assi cartesiani.

        For yScan As Single = 0 To maxValore Step 10
                                    
            gr.DrawString(yScan.ToString, labelFont, ColoreTesto, -2 * yScan.ToString.Length - 3, maxValore - 3 - yScan)
            
        Next yScan

        For xScan As Single = 0 To numeroValori * 10 Step 10

            gr.DrawString(xScan.ToString, labelFont, ColoreTesto, CType(xScan + 1.7 - 2 * xScan.ToString.Length, Single), _
            CType(maxValore + 3, Single))

        Next xScan

rilascia le risorse occupate dal font, dalla penna e dal pennello.

        labelFont.Dispose()
        ColoreTesto.Dispose()
        penBlack.Dispose()
        gr = Nothing

    End Sub

Qualora la form subisca un cambiamento delle dimensioni, anche la PictureBox, ad essa ancorata subirà una variazione dell'altezza e lunghezza, pertanto nell'evento Resize della form inseriamo il comando Refresh per ri-disegnare l'area grafica e adattarla ai nuovi valori.

    Private Sub Form1_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize

        Me.Refresh()

    End Sub

End Class

Ecco il risultato:

Immagine grafico finito

Download sorgente "Grafico_a_Barre.zip" ( 68KB )