Code for creating in-line Sparkline Charts using VB.NET 2.0/ASP.NET 2.0
A few years back I attended one of Edward Tufte's seminars. I enjoyed the seminar and while
lots of good ideas were presented the one that stuck with me most firmly was the SparkLine.
SparkLines are small in-line charts included along with text in a report. For example, a stock
price might tell you one thing, but seeing that the price is the highest that the stock has
had in 12 weeks tells you something more. Moreover, being able to see at a glance a page of these
trends might enable you to quickly pick out those stocks which are most quickly rising in value.
I really liked the sparkline idea and took away from Tufte's lecture an enhanced sense of the
importance of not wasting screen real-estate, of not requiring the user to click through multiple
screens when a single screen can show the same data and of not engaging in unnecessary prettification
of charts and reports.
Recently I started work on a new project, a dashboard for a sales and marketing department and
wanted to use sparklines to show trend information in a forecast report. I looked around and
found several sparkline implementations including this very nice one written in PHP. My dashboard
is a DotNet 2.0 application however and I didn't want to mix languages. I also wasn't sure how
I would want to present the data (line? bar? line for average? shaded bar to show range of standard deviation?)
and thought that if I rolled my own then I'd be able to take it in whatever direction I needed to.
What I present here is that sparkline implementation. After creating my sparkline chart, I found this
article on CodeProject by Igor Krupitsky. If I had found it prior to creating my sparkline chart, I probably would
have used Igor's code.
As a sample, here is the Dow Jones Utility Average (w/e 6/8) * Table courtesty finance.yahoo.com
|
Symbol
|
Name
|
Last Trade
|
Change
|
Volume
|
Last 10 Days (line)
|
Last 10 Days (bar)
|
|
AEP
|
AMER ELECTRIC POW CO
|
44.91 Jun 8
|
0.41 (0.92%)
|
2,877,985
|
|
|
|
AES
|
AES CP INC
|
21.62 Jun 8
|
0.31 (1.45%)
|
5,361,230
|
|
|
|
CNP
|
CENTERPOINT ENERGY
|
17.56 Jun 8
|
0.13 (0.75%)
|
2,266,200
|
|
|
|
D
|
DOMINION RES NEW
|
82.47 Jun 8
|
0.75 (0.92%)
|
2,896,287
|
|
|
|
DUK | DUKE ENERGY CP HL CO
|
18.38 Jun 8
|
0.00 (0.00%)
|
13,922,725
|
|
|
|
ED | CONS EDISON INC
|
46.59 Jun 8
|
0.20 (0.43%)
|
3,834,940
|
|
|
|
EIX
|
EDISON INTL
|
54.76 Jun 8
|
0.51 (0.94%)
|
2,782,822
|
|
|
|
EXC
|
EXELON CORPORATION
|
70.66 Jun 8
|
0.74 (1.06%)
|
4,587,103
|
|
|
|
FE
|
FIRSTENERGY CP
|
65.53 Jun 8
|
0.80 (1.24%)
|
2,806,404
|
|
|
|
NI
|
NISOURCE INC HLDG CO | 21.05 Jun 8
|
0.10 (0.48%)
|
2,395,235
|
|
|
|
PCG | PG&E CP
|
45.57 Jun 8
|
0.04 (0.09%)
|
3,491,900
|
|
|
|
PEG
|
PUB ENTRPR GP
|
83.29 Jun 8
|
0.86 (1.04%)
|
1,573,312
|
|
|
|
SO
|
SOUTHERN CO
|
34.60 Jun 8
|
0.29 (0.85%)
|
5,094,421
|
|
|
|
TXU
|
TXU CORP
|
67.41 Jun 8
|
0.21 (0.31%)
|
2,978,700
|
|
|
|
WMB
|
WILLIAMS COS
|
30.27 Jun 8
|
0.19 (0.63%)
|
6,421,647
|
|
|
This is the source code for the web page that actually displays the sparkline chart. The chart is displayed by referencing the page (my
page is called ILC for in-line-chart) in an img src tag as in this example:
<img src="../tools/ilc.aspx?T=L&W=100&H=20&TH=2&MN=MN&MX=MX&FG=008800&BG=FFFFFF&DP=1,2,3,4" />
The above img tag displays this chart:
The querystring parameters define the format of the chart and provide the data that composes it.
| Parameter |
Description |
| T=L |
The chart will be a line chart (the other option is B for bar). |
| W=100 |
The chart will be 100 pixels wide |
| H=20 |
The chart will be 20 pixels high |
| MN=MN |
Minimum value. "MN" is min value in chart - 2%. If omitted, 0 is used. |
| MX=MX |
Maximum value. "MX" is max value in chart + 2%. If omitted, max provided value + 2% is used. |
| TH=2 |
The trend line will be 2 pixels thick. (The default is 1) |
| FG=008800 |
The trend line will be green. |
| BG=FFFFFF |
The chart background color will be white. |
| DP=1,2,3,4 |
Specifies the values to be charted. |
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="ilc.aspx.vb" Inherits="ilc" %>
<%@ Import namespace="Microsoft.VisualBasic" %>
<%@ Import namespace="System.Drawing" %>
<%@ Import namespace="System.Drawing.Imaging" %>
<%@ Import namespace="System.Web" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
'---Page accepts these querystring values:
' T: B (bar) or L (line). If no type is specified then L is assumed.
' DP: One or more data points.
' W: Width
' H: Height
' MN: Minimum value. "MN" is min value in chart - 2%. If omitted, 0 is used.
' MX: Maximum value. "MX" is max value in chart + 2%. If omitted, max provided value + 2% is used.
' FG: Line Color
' BG: Background Color
' TH: Line thickness (line chart) or space between bars (bar chart)
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim sDPList As String = Request.QueryString("DP")
Dim sDP() As String = sDPList.Split(",")
Dim oChart As New JInLineChart
If Request.QueryString("T") = "L" Then
oChart.ChartType = JInLineChart.Type.Line
ElseIf Request.QueryString("T") = "B" Then
oChart.ChartType = JInLineChart.Type.Bar
End If
oChart.Data = sDP
oChart.Width = Request.QueryString("W")
oChart.Height = Request.QueryString("H")
oChart.LineHeight = Request.QueryString("TH")
oChart.ForegroundColor = Request.QueryString("FG")
oChart.BackgroundColor = Request.QueryString("BG")
Dim sMinMode As String = Request.QueryString("MN")
Dim sMaxMode As String = Request.QueryString("MX")
If sMinMode = "MN" Then
oChart.DisplayMin = oChart.ActualMin * 0.98
ElseIf IsNumeric(sMinMode) Then
oChart.DisplayMin = Val(sMinMode)
End If
If sMaxMode = "MX" Then
oChart.DisplayMax = oChart.ActualMax * 1.02
ElseIf IsNumeric(sMaxMode) Then
oChart.DisplayMax = Val(sMaxMode)
End If
oChart.ShowChart()
End Sub
Private Class JInLineChart
Public Enum Type As Integer
Line = 1
Bar = 2
End Enum
Private miImageWidth As Integer = 100
Private miImageHeight As Integer = 30
Private miChartX As Integer = 3
Private miChartY As Integer = 3
Private miLineWidth As Integer = 3
Private miChartWidth As Integer = miImageWidth - miChartX * 2
Private miChartHeight As Integer = miImageHeight - miChartY * 2
Private moForegroundColor As Color = Color.Black
Private moBackgroundColor As Color = Color.White
Private mdDisplayMin As Single
Private mdDisplayMax As Single
Private mlChartType As Type
Private mdData() As Single
Private miCount As Integer
Private mdActualMin As Single
Private mdActualMax As Single
Public Property ChartType() As Type
Get
Return mlChartType
End Get
Set(ByVal value As Type)
mlChartType = value
End Set
End Property
Public WriteOnly Property Data() As String()
Set(ByVal value As String())
miCount = value.GetUpperBound(0)
If miCount > 0 Then
mdActualMin = 0
mdActualMax = 0
mdActualMin = Val(value(0))
mdActualMax = Val(value(0))
ReDim mdData(miCount)
For i As Integer = 0 To miCount
If IsNumeric(value(i)) Then
mdData(i) = Val(value(i))
If mdData(i) > mdActualMax Then
mdActualMax = mdData(i)
End If
If mdData(i) < mdActualMin Then
mdActualMin = mdData(i)
End If
Else
mdData(i) = 0
End If
Next
mdDisplayMin = mdActualMin * 0.98
mdDisplayMax = mdActualMax * 1.02
Else
mdActualMin = Single.NaN
mdActualMax = Single.NaN
mdDisplayMin = Single.NaN
mdDisplayMax = Single.NaN
End If
End Set
End Property
Public WriteOnly Property Width() As Integer
Set(ByVal value As Integer)
miImageWidth = value
miChartWidth = miImageWidth - miChartX * 2
End Set
End Property
Public WriteOnly Property Height() As Integer
Set(ByVal value As Integer)
miImageHeight = value
miChartHeight = miImageHeight - miChartY * 2
End Set
End Property
Public WriteOnly Property LineHeight() As Integer
Set(ByVal value As Integer)
miLineWidth = value
End Set
End Property
Public WriteOnly Property ForegroundColor() As String
Set(ByVal value As String)
moForegroundColor = HTMLColorToColor(value)
End Set
End Property
Public WriteOnly Property BackgroundColor() As String
Set(ByVal value As String)
moBackgroundColor = HTMLColorToColor(value)
End Set
End Property
Public ReadOnly Property ActualMin() As Single
Get
Return mdActualMin
End Get
End Property
Public ReadOnly Property ActualMax() As Single
Get
Return mdActualMax
End Get
End Property
Public Property DisplayMin() As Single
Get
Return mdDisplayMin
End Get
Set(ByVal value As Single)
mdDisplayMin = value
End Set
End Property
Public Property DisplayMax() As Single
Get
Return mdDisplayMax
End Get
Set(ByVal value As Single)
mdDisplayMax = value
End Set
End Property
Public Sub ShowChart()
'---Create new image for composite
Dim oImage As Bitmap = New Bitmap(miImageWidth, miImageHeight, PixelFormat.Format24bppRgb)
'---Paste in the parts
Dim oG As Graphics = Graphics.FromImage(oImage)
oG.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias
'---Initialize graphic
oG.FillRectangle(New SolidBrush(moBackgroundColor), New Rectangle(0, 0, miImageWidth, miImageHeight))
If mlChartType = Type.Line Then
DrawLineChart(oG)
ElseIf mlChartType = Type.Bar Then
DrawBarChart(oG)
End If
HttpContext.Current.Response.ContentType = "image/JPEG"
oImage.Save(HttpContext.Current.Response.OutputStream, ImageFormat.Jpeg)
oG.Dispose()
oImage.Dispose()
End Sub
Private Sub DrawLineChart(ByRef oG As Graphics)
If miCount > 0 Then
Dim iInterval As Integer = miChartWidth / (miCount + 1)
Dim dRange As Decimal = mdDisplayMax - mdDisplayMin
If dRange > 0 Then
Dim iCurX As Integer = 0
Dim iCurY As Integer = 0
Dim iPrevX As Integer = 0
Dim iPrevY As Integer = 0
For iPoint As Integer = 0 To miCount
Dim dAdjValue As Decimal = mdData(iPoint) - mdDisplayMin
iPrevX = iCurX
iPrevY = iCurY
iCurX = iInterval * iPoint + (iInterval / 2)
iCurY = miChartHeight - (miChartHeight * (dAdjValue / dRange))
If iPoint > 0 Then
oG.DrawLine( _
New Pen(moForegroundColor, miLineWidth), _
miChartX + iPrevX, _
miChartY + iPrevY, _
miChartX + iCurX, _
miChartY + iCurY)
End If
Next
End If
End If
End Sub
Private Sub DrawBarChart(ByRef oG As Graphics)
If miCount > 0 Then
Dim iInterval As Integer = miChartWidth / (miCount + 1)
Dim dRange As Decimal = mdDisplayMax - mdDisplayMin
If dRange > 0 Then
For iPoint As Integer = 0 To miCount
Dim dAdjValue As Decimal = mdData(iPoint) - mdDisplayMin
Dim iCurX As Integer = iInterval * iPoint + (iInterval / 2)
Dim iCurY As Integer = miChartHeight * (dAdjValue / dRange)
Dim oRect As New Rectangle( _
miChartX + iCurX - iInterval / 2 + miLineWidth, _
miChartY + (miChartHeight - iCurY), _
iInterval - miLineWidth * 2, _
iCurY)
oG.FillRectangle(New SolidBrush(moForegroundColor), oRect)
Next
End If
End If
End Sub
Private Function HTMLColorToColor(ByVal sColor As String) As Color
Dim iRed As Integer = CLng("&H" & sColor.Substring(0, 2))
Dim iGreen As Integer = CLng("&H" & sColor.Substring(2, 2))
Dim iBlue As Integer = CLng("&H" & sColor.Substring(4, 2))
Return Color.FromArgb(iRed, iGreen, iBlue)
End Function
End Class
</script>