Appunti di Programmazione

Creative Commons License

Creiamo un grafico a torta

Con questo breve tutorial, illustro come creare dei semplici Grafici a Torta usando le istruzioni grafiche del GDI+.

Avviamo un nuovo progetto Windows Application e inseriamo il seguente codice:

Public Class Form1

    'definizione dei controlli
    Private WithEvents PictureBox1 As New PictureBox
    Private WithEvents GroupBox1 As New GroupBox
    Private WithEvents rbVisioneCompatta As New RadioButton
    Private WithEvents rbVisioneEsplosa As New RadioButton

    'dati da elaborare e rappresentare nel grafico a torta
    Dim datiGrafico() As Single = {12000, 6000, 6900, 7800}

    'dati elaborati per essere e trasformati in percentuali e rappresentati nel grafico a torta
    Dim datiPercentuali(0 To 3) As Single

    'colore di primo piano del grafico
    Dim colori() As System.Drawing.Color = {Color.Blue, Color.Red, Color.Green, Color.Yellow}

    'colore del bordo del grafico
    Dim coloriScuri() As System.Drawing.Color = {Color.DarkBlue, Color.DarkRed, Color.DarkGreen, Color.YellowGreen}

    'etichette dei vari spicchi del grafico a torta
    Dim nomiBarre() As String = {"Proteine", "Lipidi", "Carboidrati", "Alcol"}

    Private Sub Grafico_a_Torte_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        'si aggiungono i controlli alla Form
        Me.Size = New Size(500, 500)
        Me.Text = "Grafico a Torta"

        PictureBox1.Size = New Size(450, 275)
        PictureBox1.BackColor = Color.Black
        PictureBox1.Location = New Point(21, 19)

        GroupBox1.Text = "Visualizzazione"
        GroupBox1.Size = New Size(200, 110)
        GroupBox1.Location = New Point(146, 320)

        rbVisioneCompatta.Text = "Compatta"
        rbVisioneCompatta.Checked = True
        rbVisioneCompatta.Location = New Point(20, 28)

        rbVisioneEsplosa.Text = "Esplosa"
        rbVisioneEsplosa.Checked = False
        rbVisioneEsplosa.Location = New Point(20, 66)

        Me.Controls.Add(PictureBox1)
        Me.Controls.Add(GroupBox1)
        Me.GroupBox1.Controls.Add(rbVisioneCompatta)
        Me.GroupBox1.Controls.Add(rbVisioneEsplosa)

        CreazioniSpicchi(datiGrafico)

    End Sub

    Private Sub CreazioniSpicchi(ByRef dati() As Single)

        'conversioni dei dati in valori percentuali rappresentabili sul grafico:
        'si recupera il totale...
        Dim somma As Single = 0
        For Each d As Single In dati
            somma += d
        Next

        '...e lo si usa per determinare le varie percentuali
        For i As Integer = 0 To 3
            datiPercentuali(i) = 360 * datiGrafico(i) / somma
        Next

    End Sub

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

        'ogni spicchio per essere disegnato ha bisogno di un angolo iniziale e dell'ampiezza dell'angolo
        'definizione dell'angolo iniziale
        Dim angoloIniziale As Single

        'definizione della bisettrice di ogni spicchio
        Dim angoloBisettrice As Single

        'coordinate del centro dello spicchio
        Dim punto As New PointF

        'per realizzare l'effetto 3D si disegnano 31 grafici a torta uno sopra all'altro e traslati fra di loro di un pixel.
        'l'ultimo grafico viene disegnato con un colore più chiaro per dare l'illusione della vista prospettica
        For z As Integer = 130 To 100 Step -1

            angoloIniziale = 0
            angoloBisettrice = 0

            For i As Integer = 0 To 3

                angoloBisettrice += datiPercentuali(i) / 2

                'nella visione esplosa gli spicchi non formano un ellisse compatto ma sono separati gli uni dagli altri
                'ogni vertice giace su un ellisse più piccola concentrica a quella che rappresenta il grafico stesso
                'con il blocco seguente viene calcolato il punto in cui giace il vertice dello spicchio
                Select Case angoloBisettrice
                    Case 0 To 90
                        punto.X = Math.Cos(DecToRad(angoloBisettrice)) * 25
                        punto.Y = Math.Sin(DecToRad(angoloBisettrice)) * 10
                    Case 90 To 180
                        punto.X = -Math.Cos(DecToRad(180 - angoloBisettrice)) * 25
                        punto.Y = Math.Sin(DecToRad(180 - angoloBisettrice)) * 10
                    Case 180 To 270
                        punto.X = -Math.Cos(DecToRad(angoloBisettrice - 180)) * 25
                        punto.Y = -Math.Sin(DecToRad(angoloBisettrice - 180)) * 10
                    Case Is > 270
                        punto.X = Math.Cos(DecToRad(360 - angoloBisettrice)) * 25
                        punto.Y = -Math.Sin(DecToRad(360 - angoloBisettrice)) * 10
                End Select

                'se è selezionata l'opzione Visualizzazione Compatta...
                If rbVisioneCompatta.Checked Then
                    'se il grafico da disegnare è quello più in alto (=ultimo)...
                    If z = 100 Then
                        'si disegna il grafico con colori chiari...
                        e.Graphics.FillPie(New SolidBrush(colori(i)), 100, z, 250, 100, angoloIniziale, datiPercentuali(i))
                    Else
                        'altrimenti si disegna il grafico con colori scuri realizzando il bordo della "Torta"
                        e.Graphics.FillPie(New SolidBrush(coloriScuri(i)), 100, z, 250, 100, angoloIniziale, datiPercentuali(i))
                    End If
                    'altrimenti se è selezionata l'opzione Visualizzazione Esplosa...
                Else
                    'se il grafico da disegnare è quello più in alto (=ultimo)...
                    If z = 100 Then
                        'si disegna il grafico con colori chiari...e si traslano gli spicchi portando i loro vertici nel Punto calcolato
                        'dal blocco Select Case
                        e.Graphics.FillPie(New SolidBrush(colori(i)), 100 + punto.X, z + punto.Y, 250, 100, angoloIniziale, _
                        datiPercentuali(i))
                    Else
                        'come sopra ma con i colori scuri
                        e.Graphics.FillPie(New SolidBrush(coloriScuri(i)), 100 + punto.X, z + punto.Y, 250, 100, angoloIniziale, _
                        datiPercentuali(i))
                    End If
                End If

                'si aggiornano i valori dei due angoli
                angoloIniziale += datiPercentuali(i)
                angoloBisettrice += datiPercentuali(i) / 2

            Next

        Next

        'dimensione in pixel delle stringhe che rappresentano i valori numerici da disegnare in prossimità degli spicchi
        Dim dimensioneStringa As New SizeF

        'distanza del vertice del rettangolo che contiene la scritta, dal centro dell'ellisse quando il grafico è compatto
        Dim distanza As New PointF
        angoloBisettrice = 0

        For i As Integer = 0 To 3

            'si recuperano le dimensioni della stringa che sarà disegnata in modo da poterla rappresentare esternamente
            'al grafico ma con uno spigolo sempre vicino al bordo del grafico stesso
            dimensioneStringa = e.Graphics.MeasureString(datiGrafico(i).ToString, New Font("Arial", 8, FontStyle.Regular))
            angoloBisettrice += datiPercentuali(i) / 2

            'se è selezionata l'opzione Visualizzazione Compatta...
            If rbVisioneCompatta.Checked Then
                'Si imposta la distanza del testo dal centro dell'ellisse
                distanza.X = 125
                distanza.Y = 50
            Else
                'altrimenti si aumenta tale distanza per compensare l'"Eslposione" del grafico
                distanza.X = 150
                distanza.Y = 60
            End If

            'gli spicchi "Esplodono" muovendosi sulla bisettrice dell'angolo che coprono, e spostandosi in modo
            'che il loro vertice si posizioni sul nuovo "Punto"
            Select Case angoloBisettrice
                Case 0 To 90
                    punto.X = Math.Cos(DecToRad(angoloBisettrice)) * distanza.X + 225
                    punto.Y = Math.Sin(DecToRad(angoloBisettrice)) * distanza.Y + 150
                Case 90 To 180
                    punto.X = -Math.Cos(DecToRad(180 - angoloBisettrice)) * distanza.X + 225 - dimensioneStringa.Width
                    punto.Y = Math.Sin(DecToRad(180 - angoloBisettrice)) * distanza.Y + 150
                Case 180 To 270
                    punto.X = -Math.Cos(DecToRad(angoloBisettrice - 180)) * distanza.X + 225 - dimensioneStringa.Width
                    punto.Y = -Math.Sin(DecToRad(angoloBisettrice - 180)) * distanza.Y + 150 - dimensioneStringa.Height
                Case Is > 270
                    punto.X = Math.Cos(DecToRad(360 - angoloBisettrice)) * distanza.X + 225
                    punto.Y = -Math.Sin(DecToRad(360 - angoloBisettrice)) * distanza.Y + 150 - dimensioneStringa.Height
            End Select

            'si disegna il testo
            e.Graphics.DrawString(datiGrafico(i).ToString, New Font("Arial", 8, FontStyle.Regular), Brushes.WhiteSmoke, _
            punto.X, punto.Y)
            angoloBisettrice += datiPercentuali(i) / 2

        Next

        'si disegna la legenda
        For i As Integer = 0 To 3

          e.Graphics.FillRectangle(New SolidBrush(colori(i)), 360, (i + 1) * 15 + 10, 10, 10)
          e.Graphics.DrawString(nomiBarre(i), New Font("Arial", 8, FontStyle.Regular), Brushes.White, 380, (i + 1) * 15 + 10)

        Next

    End Sub

    'conversione fra angoli sessagesimali e radianti
    Private Function DecToRad(ByVal angolo As Single) As Double

        Return (angolo * Math.PI / 180)

    End Function

    'si ridisegna l'area se cambia lo stato di un'opzione
    Private Sub CambioOpzione(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles rbVisioneCompatta.CheckedChanged, rbVisioneEsplosa.CheckedChanged

        PictureBox1.Invalidate()

    End Sub

End Class

Ecco il risultato:

Immagine grafico compatto finito

Immagine grafico esploso finito

Download sorgente "Grafico_a_Torta.zip" ( 73KB )