Graphique facile a dessiner : camembert et histogramme

Description

Ce code permet de dessiner des camemberts et des histogramme facilement sans aucune API! certe c'est un peu réinventer la roue mais si comme moi vous n'arrivez pas a faire marche les objets fait pour afficher des graphe vous serez content de le trouver. Son principe est simple : il dessine lui même le camembert ou l'histogramme à même la form.

Source / Exemple :


Public Class Chart
    Inherits CollectionBase
    Implements IDisposable
    'hérite de la classe CollectionBase pour être une liste de ainsi profiter de toutes les méthode
    'de cette classe

    'liste et énumerateur pour les couleur
    Dim LLCouleur As LinkedList(Of Color)
    Private couleur As LinkedList(Of Color).Enumerator
    'autre variables
    Public ShowText As Boolean
    Public TextMode As TypeText
    Public GraphMode As TypeGraph = TypeGraph.Pie2D
    Public Font As Font

    'Enum afin de définir le type d'affichage du texte : %, val, les deux, ...
    Public Enum TypeText
        Pourcent = 0
        Valeur = 1
        PourcentPuisValeur = 2
        ValeurPuisPourcent = 3
        Pourcent_SautLigne_Valeur = 4
        Valeur_SautLigne_Pourcent = 6
    End Enum

    'type du graphique : histogramme (barres), camembert, d'autre aisément ajoutable
    Public Enum TypeGraph
        Pie2D = 0
        Hist2D = 1
    End Enum

    Public Sub New(ByVal FontA As Font)
        Font = FontA
        'list des couleurs que l'on affichera
        LLCouleur = New LinkedList(Of Color)
        LLCouleur.AddLast(Color.Thistle)
        LLCouleur.AddLast(Color.Aqua)
        LLCouleur.AddLast(Color.Coral)
        LLCouleur.AddLast(Color.DeepSkyBlue)
        LLCouleur.AddLast(Color.DarkSalmon)
        LLCouleur.AddLast(Color.DarkOrchid)
        couleur = LLCouleur.GetEnumerator
    End Sub

    'ajoute de nv item au graph en spécifiant la couleur
    Public Function Add(ByVal iValeur As Integer, ByVal Couleur As Color, Optional ByVal texte As String = "") As ChartItem
        Dim oCI As New ChartItem(iValeur, Couleur, texte)
        Me.List.Add(oCI)
        Return oCI
    End Function

    'se déclanche quand on a finit de vider un graph de ces items
    Protected Overrides Sub OnClearComplete()
        MyBase.OnClearComplete()
        couleur = LLCouleur.GetEnumerator
    End Sub

    'ajout de nv item sans spécifier la couleur
    Public Function Add(ByVal iValeur As Integer, Optional ByVal texte As String = "") As ChartItem
        'si on est arriver a la fin de la liste des couleurs on revient au début
        ' plus détaillé : quand le curseur à été parcouru on crée un nouveau curseur et on le position sur la 
        ' première couleur de la liste
        If Not (couleur.MoveNext) Then
            couleur = LLCouleur.GetEnumerator
            couleur.MoveNext()
        End If
        Return Me.Add(iValeur, couleur.Current, texte)
    End Function

    'accesseur en lecture seul pour les items
    Default Public ReadOnly Property Item(ByVal iIndex As Integer) As ChartItem
        Get
            Return CType(Me.List.Item(iIndex), ChartItem)
        End Get
    End Property

    'calcule le total de valeur pour les items
    'on peut imaginer une évolution où l'utilisateur définirai lui même un max et si ce dernier est dépassé alors
    'et alors seulement on prend le mac réél
    Public ReadOnly Property Total() As Integer
        Get
            Dim iTotal As Integer, iItem As ChartItem
            For Each iItem In Me.List
                iTotal += iItem.Valeur
            Next
            Return iTotal
        End Get
    End Property

    'demande le dessin du graphique en fonction du type définit en paramètre
    Public Sub Draw(ByVal g As Graphics, ByVal oRect As Rectangle, ByVal type As TypeGraph)
        'g.DrawRectangle(System.Drawing.Pens.Black, oRect)
        oRect.Height = oRect.Height - 2 * g.MeasureString("Test01", Me.Font).Height
        oRect.Y = oRect.Y + g.MeasureString("Test01", Me.Font).Height
        Select Case type
            Case TypeGraph.Hist2D
                DrawHist2D(g, oRect)
            Case TypeGraph.Pie2D
                'on tranforme le rectangle en carré pour avoir un rond et non un oval
                If oRect.Height < oRect.Width Then
                    oRect.X += (oRect.Width - oRect.Height) / 2
                    oRect.Width = oRect.Height
                Else
                    oRect.Y += (oRect.Height - oRect.Width) / 2
                    oRect.Height = oRect.Width
                End If
                DrawPie2D(g, oRect)
        End Select
    End Sub

    'demande le dessin du graphique en fonction du type définit avant
    Public Sub Draw(ByVal g As Graphics, ByVal oRect As Rectangle)
        On Error Resume Next
        'on retrouve assez régulièrement des divisions par 0 dans les graphs si leurs tailles est petites
        'on on passe en cas de problème
        Draw(g, oRect, Me.GraphMode)
    End Sub

    'dessin l'histogramme
    Private Sub DrawHist2D(ByVal g As Graphics, ByVal oRect As Rectangle)
        Dim iItem As ChartItem
        Dim iTotal As Integer = Total
        Dim iMax As Integer = Integer.MinValue
        Dim iRec As Rectangle
        Dim iCpt As Integer = 0
        Dim iLarg As Integer
        Dim iUnite As Integer
        Dim iX As Integer
        Dim iPas As Integer = 0
        Dim iText As String = 0
        Dim iVal As String = 0

        For Each iItem In Me.List
            If iItem.Valeur > iMax Then iMax = iItem.Valeur
            iCpt = iCpt + 1
        Next
        iLarg = Math.Min(((oRect.Width - 20) / iCpt), 40)
        iPas = (oRect.Width - 20 - iLarg * iCpt) / iCpt
        iUnite = oRect.Height / iMax
        iX = oRect.X + 10 + iPas / 2
        iRec.Width = iLarg
        'cadre
        'ordonné
        g.DrawLine(Pens.Black, oRect.X, oRect.Y, oRect.X, oRect.Y + oRect.Height)
        'abcisse
        g.DrawLine(Pens.Black, oRect.X + oRect.Width, oRect.Y + oRect.Height, oRect.X, oRect.Y + oRect.Height)

        For Each iItem In Me.List
            'la barre
            iRec.X = iX
            iRec.Y = oRect.Y + (iMax - iItem.Valeur) * iUnite
            iRec.Height = oRect.Y + oRect.Height - iRec.Y 'iItem.Valeur * iUnite
            g.FillRectangle(iItem.Brush, iRec)
            g.DrawRectangle(Pens.Black, iRec)

            Dim sngAngle As Single = 360.0F * iItem.Valeur / iTotal

            'le texte
            If ShowText Then
                iVal = getTextVal(iItem, iTotal, TypeText.Valeur)
                iText = iItem.texte

                g.DrawString(iVal, Me.Font, Brushes.Black, iRec.X + iRec.Width / 2 - g.MeasureString(iVal, Me.Font).Width / 2, iRec.Y + iRec.Height / 2 - g.MeasureString(iVal, Me.Font).Height / 2)
                g.DrawString(iText, Me.Font, Brushes.Black, iRec.X + iRec.Width / 2 - g.MeasureString(iText, Me.Font).Width / 2, iRec.Y + iRec.Height + g.MeasureString(iText, Me.Font).Height / 2)
            End If
            iX += iPas + iLarg
        Next

    End Sub

    'dessin le camembert
    Private Sub DrawPie2D(ByVal g As Graphics, ByVal oRect As Rectangle)
        Dim iItem As ChartItem, sngCurrentStartAngle As Single
        Dim iTotal As Integer = Total
        For Each iItem In Me.List
            'le camembert
            Dim sngAngle As Single = 360.0F * iItem.Valeur / iTotal
            g.FillPie(iItem.Brush, oRect, sngCurrentStartAngle, sngAngle)
            g.DrawPie(Pens.Black, oRect, sngCurrentStartAngle, sngAngle)

            'le texte
            If ShowText Then
                Dim sText As String = ""
                sText = iItem.texte + " : " + getTextVal(iItem, iTotal)

                'position du texte
                Dim sngX, sngY As Single, sngAngleRadian As Single
                sngAngleRadian = CType((sngCurrentStartAngle + sngAngle / 2) * Math.PI / 180, Single)
                sngX = CType(oRect.X + oRect.Width / 2, Single)
                sngX += CType((oRect.Width / 2) * Math.Cos(sngAngleRadian), Single)
                If Math.Cos(sngAngleRadian) < 0 Then
                    sngX -= g.MeasureString(sText, Me.Font).Width
                End If
                sngY = CType(oRect.Y + oRect.Height / 2, Single)
                sngY += CType((oRect.Height / 2) * Math.Sin(sngAngleRadian), Single)
                If Math.Sin(sngAngleRadian) < 0 Then
                    sngY -= g.MeasureString(sText, Me.Font).Height
                End If

                g.DrawString(sText, Me.Font, Brushes.Black, sngX, sngY)
            End If

            sngCurrentStartAngle += sngAngle
        Next
    End Sub

    'un sort de garbage collector : libert la mémoire utiliser par lui et ses objets
    Public Sub Dispose() Implements IDisposable.Dispose
        Dim iItem As ChartItem
        For Each iItem In Me.List
            iItem.Dispose()
        Next
        Me.List.Clear()
    End Sub

    'retourn le texte a affiché dans les graphique en fonction du type définit avant
    Private Function getTextVal(ByRef iItem As ChartItem, ByVal iTotal As Integer) As String
        Return getTextVal(iItem, iTotal, Me.TextMode)
    End Function

    'retourn le texte a affiché dans les graphique en fonction du type passé en paramètre
    Private Function getTextVal(ByRef iItem As ChartItem, ByVal iTotal As Integer, ByRef leTextMode As TypeText) As String
        Select Case leTextMode
            Case TypeText.Pourcent
                Return (100 * iItem.Valeur / iTotal).ToString("#.#") & " %"

            Case TypeText.Valeur
                Return iItem.Valeur.ToString

            Case TypeText.PourcentPuisValeur
                Return (100 * iItem.Valeur / iTotal).ToString("#.#") & " %" + _
                        " (" + iItem.Valeur.ToString + "/" + iTotal.ToString + ")"

            Case TypeText.Pourcent_SautLigne_Valeur
                Return (100 * iItem.Valeur / iTotal).ToString("#.#") & " %" + vbNewLine + _
                        "(" + iItem.Valeur.ToString + "/" + iTotal.ToString + ")"

            Case TypeText.Valeur_SautLigne_Pourcent
                Return iItem.Valeur.ToString + vbNewLine + "(" + (100 * iItem.Valeur / iTotal).ToString("#.#") & " %" + ")"
        End Select
        'case TypeText.ValeurPuisPourcent
        Return iItem.Valeur.ToString + " (" + (100 * iItem.Valeur / iTotal).ToString("#.#") & " %" + ")"
    End Function
End Class

Public Class ChartItem
    Implements IDisposable
    'objet du graphique : ce sera une "part" du camembert, une barre de l'histogramme, ...
    Public Valeur As Integer
    Public texte As String
    Private _Couleur As Color
    Private _Brush As Brush

    Public Sub New()
        Couleur = Color.White   'couleur par défaut
    End Sub

    Public Sub New(ByVal iValeur As Integer, ByVal CouleurA As Color, ByVal newTexte As String)
        Me.New()
        Valeur = iValeur
        Couleur = CouleurA
        texte = newTexte
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        If Not _Brush Is Nothing Then _Brush.Dispose()
    End Sub

    Public Property Couleur() As Color
        Get
            Return _Couleur
        End Get
        Set(ByVal Value As Color)
            _Couleur = Value
        End Set
    End Property

    Public Property Brush() As Brush
        Get
            If _Brush Is Nothing Then
                Return New SolidBrush(Me.Couleur)
            End If
            Return _Brush
        End Get
        Set(ByVal Value As Brush)
            _Brush = Value
        End Set
    End Property
End Class

Conclusion :


Le code initial n'est pas de moi, je l'ai récupéré et j'ai ajouté la fonction d'histogramme, et la "génération" automatique de couleur.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.