📄 charthelper.vb
字号:
Imports System.Drawing.Drawing2D
' Constants.
Friend Class ConstValues
' How many pixels a chart item expands.
Public Shared ExpandSize As Integer = 10
' Size of 3D shadow.
Public Shared ShadowSize As Integer = 3
End Class
' Color table that is used for chart items. User of the control
' can override the color table with methods exposed from the main
' control class. Colors repeat if an index is specified that is
' larger than the color table but the color is slightly brighter.
Friend Class ChartColors
#Region " Color Table "
Private m_List As Color() = _
{ _
Color.Tomato, _
Color.LimeGreen, _
Color.Orange, _
Color.DarkSeaGreen, _
Color.SteelBlue, _
Color.MediumOrchid, _
Color.Goldenrod, _
Color.LightBlue, _
Color.LightGreen, _
Color.Coral, _
Color.Aquamarine, _
Color.DeepSkyBlue, _
Color.LightSalmon, _
Color.SkyBlue _
}
#End Region
' Override a color in the color table.
Public Sub SetColor(ByVal Index As Integer, ByVal NewColor As Color)
' Return right away if passed in an invalid index.
If (Index < 0) Or (Index >= m_List.Length) Then
Return
End If
m_List(Index) = NewColor
End Sub
' Return color from color table. Colors repeat but are slightly brighter.
Public Function GetColor(ByVal Index As Integer) As Color
Dim result As Color = m_List((Index Mod m_List.Length))
Dim x As Integer = CInt(Index / m_List.Length)
If x > 0 Then
' Asked for a color larger than our internal table.
result = Color.FromArgb( _
Math.Min(result.R + x, 255), _
Math.Min(result.G + x, 255), _
Math.Min(result.B + x, 255))
End If
Return result
End Function
End Class
' Base class that performs drawing functions. Shape classes
' inherit from this class and perform shape-specific actions,
' calculate position / size and draw shapes.
Friend Class ChartDrawing
' Used to access main control properties.
Protected m_Parent As CustomChart = Nothing
' Constructor.
Public Sub New(ByVal Parent As CustomChart)
m_Parent = Parent
End Sub
' Draw the chart.
Public Overridable Sub Draw(ByVal Width As Integer, _
ByVal Height As Integer, ByVal rc As Rectangle, _
ByVal g As Graphics, ByVal HitTest As Graphics)
' Don't try drawing anything if area is too small.
If (rc.Width <= 1) Or (rc.Height <= 1) Then
Return
End If
' First, draw the 3D shadow.
Dim item As ChartItem
Dim i As Integer
For i = ConstValues.ShadowSize To 0 Step -1
For Each item In m_Parent.List
' Get drawing area for this item.
Dim rcShadow As New Rectangle(rc.Location, rc.Size)
rcShadow.Offset(i, i)
If item.IsExpanded Then
' Draw expanded item shadow.
Dim rcShift As New Rectangle(rcShadow.Location, rcShadow.Size)
rcShift.Offset(item.ExpandOffset.X, item.ExpandOffset.Y)
DrawShape(g, New HatchBrush(HatchStyle.Percent50, item.Color), rcShift, item)
Else
' Draw item shadow.
DrawShape(g, New HatchBrush(HatchStyle.Percent50, item.Color), rcShadow, item)
End If
Next item
Next i
' Now go through and draw chart items.
For Each item In m_Parent.List
' Adjust the bounding rectangle for expanded items. If the
' item is expanded, draw on a memory bitmap with non-expanded
' area so the expanded area acts like it's part of the
' shape (used when performing hit testing later).
Dim rcShape As New Rectangle(rc.Location, rc.Size)
If item.IsExpanded Then
rcShape.Offset(item.ExpandOffset.X, item.ExpandOffset.Y)
DrawShape(HitTest, New SolidBrush(item.Color), rc, item)
End If
' Use highlight brush if mouseover, otherwise use gradient brush.
Dim brushShape As Brush = Nothing
If item.IsOver Then
brushShape = New SolidBrush(item.HighlightColor)
Else
brushShape = New LinearGradientBrush(rcShape, item.Color, item.HighlightColor, LinearGradientMode.Horizontal)
End If
' Draw the shape.
DrawShape(g, brushShape, rcShape, item)
DrawShape(HitTest, New SolidBrush(item.Color), rcShape, item)
Next item
' Draw the shape labels.
' Calculate border size used when drawing labels.
Dim border As Integer = ConstValues.ExpandSize + ConstValues.ShadowSize + 1
DrawLabels(g, rc, border, m_Parent.List)
End Sub
' Draws labels, values and percent in each shape. All three values can be
' turned on / off through properties exposed by the main control class.
Private Sub DrawLabels(ByVal g As Graphics, ByVal rc As Rectangle, _
ByVal Border As Integer, ByVal List As ArrayList)
' Calculate total value of chart (sum of all items) that is
' used when drawing percent.
Dim total As Integer = m_Parent.TotalValue
' Go through each item and draw label for each item in chart.
Dim item As ChartItem
For Each item In List
' Create the label string to display based on control properties.
Dim labelStr As String = String.Empty
If m_Parent.ShowLabel Then
labelStr = item.Label
End If
' Create the value string to display based on control properties.
Dim valueStr As String = String.Empty
If (m_Parent.ShowValue = True) And (m_Parent.ShowPercent = False) Then
valueStr = item.Value.ToString()
Else
If (m_Parent.ShowValue = False) And (m_Parent.ShowPercent = True) Then
valueStr = String.Format("{0:0}%", (item.Value * 100) / total)
Else
If (m_Parent.ShowValue = True) And (m_Parent.ShowPercent = True) Then
valueStr = String.Format("{0} ({1:0}%)", item.Value, (item.Value * 100) / total)
End If
End If
End If
' Calculate size of strings.
Dim labelSize As SizeF = g.MeasureString(labelStr, m_Parent.Font)
Dim valueSize As SizeF = g.MeasureString(valueStr, m_Parent.Font)
' Calculate where to draw strings.
Dim pt As Point = item.CenterPoint
Dim offset As New Point(Border, Border)
If item.IsExpanded Then
offset.X += item.ExpandOffset.X
offset.Y += item.ExpandOffset.Y
End If
' Draw strings in chart item.
g.DrawString(labelStr, m_Parent.Font, New SolidBrush(m_Parent.ForeColor), pt.X - labelSize.Width / 2 + offset.X, pt.Y - (labelSize.Height + valueSize.Height) / 2 + offset.Y)
g.DrawString(valueStr, m_Parent.Font, New SolidBrush(m_Parent.ForeColor), pt.X - valueSize.Width / 2 + offset.X, pt.Y - (labelSize.Height + valueSize.Height) / 2 + labelSize.Height + offset.Y)
Next item
End Sub
' Return bounding rectangle for entire chart.
Public Overridable Function GetChartRectangle( _
ByVal Width As Integer, ByVal Height As Integer) As Rectangle
Return New Rectangle(0, 0, Width, Height)
End Function
' Override in derived class.
Public Overridable Sub LayoutItems(ByVal rc As Rectangle)
System.Diagnostics.Debug.Assert(False)
End Sub
' Override in derived class.
Public Overridable Sub DrawShape(ByVal g As Graphics, ByVal b As Brush, ByVal rc As Rectangle, ByVal item As ChartItem)
System.Diagnostics.Debug.Assert(False)
End Sub
' Override in derived class.
Public Overridable Sub DrawEmptyChart(ByVal g As Graphics, ByVal rc As Rectangle)
System.Diagnostics.Debug.Assert(False)
End Sub
End Class
' Pie chart class, calculates layout and draws pie shapes.
Friend Class PieDrawing
Inherits ChartDrawing
' Constructor.
Public Sub New(ByVal parent As CustomChart)
MyBase.New(parent)
End Sub
' Calculate the position of each item in the chart.
Public Overrides Sub LayoutItems(ByVal rc As Rectangle)
' Return right away if area is too small.
If (rc.Width <= 1) Or (rc.Height <= 1) Then
Return
End If
Dim start As Single = 0.0F
Dim sweep As Single = 0.0F
Dim item As ChartItem
' Go through each item and calculate layout.
For Each item In m_Parent.List
' Calculate the sweep angle for this item.
sweep = CSng(item.Value * 360) / m_Parent.TotalValue
' Calculate the offset when this item is expanded.
Dim shift As Point = GetPoint(start + sweep / 2, rc.Width, rc.Height)
' Relative to center of chart.
shift.X -= CInt(rc.Width / 2)
shift.Y -= CInt(rc.Height / 2)
' Now convert to max offset.
Dim x As Single = CSng(ConstValues.ExpandSize) / CSng(Math.Max(Math.Abs(shift.X), Math.Abs(shift.Y)))
shift.X = CInt(CSng(shift.X) * x)
shift.Y = CInt(CSng(shift.Y) * x)
item.ExpandOffset = shift
' Calculate center of pie slice.
Dim center As Point = GetPoint(start + sweep / 2, rc.Width, rc.Height)
center.X = CInt(((rc.Right - rc.Left) / 2 + center.X) / 2) + rc.Left
center.Y = CInt(((rc.Bottom - rc.Top) / 2 + center.Y) / 2)
item.CenterPoint = center
' Starting position and sweep size, both in degrees.
item.StartPos = start
item.SweepSize = sweep
start += sweep
Next item
End Sub
' Draw a pie shape.
Public Overrides Sub DrawShape(ByVal g As Graphics, ByVal br As Brush, ByVal rc As Rectangle, ByVal item As ChartItem)
' Make sure we have a valid area to draw on.
If (rc.Width > 1) And (rc.Height > 1) Then
'rc.Width = rc.Height
g.FillPie(br, rc, item.StartPos, item.SweepSize)
End If
End Sub
' Draw what is displayed when there are no items in the chart.
Public Overrides Sub DrawEmptyChart(ByVal g As Graphics, ByVal rc As Rectangle)
If (rc.Width > 1) And (rc.Height > 1) Then
g.DrawEllipse(New Pen(Color.DarkGray), rc)
End If
End Sub
' Return bounding rectangle for entire chart.
Public Overrides Function GetChartRectangle( _
ByVal Width As Integer, ByVal Height As Integer) As Rectangle
' Make chart area a square so the pie chart is a circle and not ellipse.
' Center the bounding rectangle in available space.
Dim size As Integer = Math.Min(Width, Height)
Return New Rectangle(CInt((Width - size) / 2), _
CInt((Height - size) / 2), size, size)
End Function
' Return point on circle edge given an angle.
Private Function GetPoint(ByVal Angle As Single, ByVal Width As Integer, ByVal Height As Integer) As Point
Dim topCenter As New Point(CInt(Width / 2), 0)
Dim rad As Double = Math.PI * 2 * Angle / 360
Dim pt As New Point()
pt.X = CInt((Math.Cos(rad) * topCenter.X - Math.Sin(rad) * topCenter.Y) + Width / 2)
pt.Y = CInt((Math.Sin(rad) * topCenter.X + Math.Cos(rad) * topCenter.Y) + Height / 2)
Return pt
End Function
End Class
' Stacked bar chart class, calculates layout and draws bar shapes.
Friend Class BarDrawing
Inherits ChartDrawing
' Constructor.
Public Sub New(ByVal parent As CustomChart)
MyBase.New(parent)
End Sub
' Calculate the position of each item in the chart.
Public Overrides Sub LayoutItems(ByVal rc As Rectangle)
' Return right away if area is too small.
If (rc.Width <= 1) Or (rc.Height <= 1) Then
Return
End If
' Go through each item and calculate layout.
Dim startPos As Single = 0.0F
Dim item As ChartItem
For Each item In m_Parent.List
' The starting position (top of rectangle) and height of shape are in pixels.
item.StartPos = startPos
item.SweepSize = CSng(item.Value * rc.Height) / m_Parent.TotalValue
' Calculate the offset when this item is expanded.
item.ExpandOffset = New Point(ConstValues.ExpandSize, 0)
' Center of this shape.
item.CenterPoint = New Point(CInt(rc.Width / 2), rc.Height - CInt(item.StartPos + item.SweepSize / 2))
startPos += item.SweepSize
Next item
End Sub
' Draw bar shape.
Public Overrides Sub DrawShape(ByVal g As Graphics, ByVal br As Brush, ByVal rc As Rectangle, ByVal item As ChartItem)
' Make sure we have a valid area to draw on
If (rc.Width > 1) And (rc.Height > 1) Then
g.FillRectangle(br, rc.Left, rc.Bottom - CInt(item.StartPos) - CInt(item.SweepSize), rc.Width, CInt(item.SweepSize))
End If
End Sub
' Draw what is displayed when there are no items in the chart.
Public Overrides Sub DrawEmptyChart(ByVal g As Graphics, ByVal rc As Rectangle)
If (rc.Width > 1) And (rc.Height > 1) Then
g.DrawRectangle(New Pen(Color.DarkGray), rc)
End If
End Sub
End Class
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -