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.
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.