Line Joins
The power of the GraphicsPath now becomes more obvious. In order to add a small circle - or rectangle, square or even text - in between each line segment we only need to make three tiny tweaks to the code. The first tweak adds the initial circle at the very start of the chart line; the second tweak is inserted into the loop so that a small circle is added to the start of each subsequent line segment; the third tweak pops a circle on the end of the final line segment.
Check out the following code snippet, which forms part of the original DrawTheLine procedure. I have highlighted in red the three new lines. All other code in that procedure remains unchanged.
' Create a GraphicsPath to hold the line info
Dim MyPath As New GraphicsPath
' Manually add the first circle to the path
MyPath.AddEllipse(XPosStart - 2, YPosStart - 2, 4, 4)
' Manually add the first line to the Path
MyPath.AddLine(XPosStart, YPosStart, XPosEnd, YPosEnd)
For i As Integer = 1 To UBound(Sales) - 1
' Update the X and Y positions for the next value:
' Move start point one line width to the right
XPosStart = XPosEnd
' Move end point one line width to the right
XPosEnd = CInt(XPosStart + LineWidth)
' Assign YPosStart the 'old' value of Y
YPosStart = YPosEnd
' Assign YPosEnd the next the next scaled Sales figure
YPosEnd = CInt(Sales(i + 1) * VertScale)
' Add next circle
MyPath.AddEllipse(XPosStart - 2, YPosStart - 2, 4, 4)
' Add the next line segment to the GraphicsPath
MyPath.AddLine(XPosStart, YPosStart, XPosEnd, YPosEnd)
Next
' Finally, manually add the last circle
MyPath.AddEllipse(XPosEnd - 2, YPosEnd - 2, 4, 4)
The AddEllipse method adds a circle with a diameter of 4 pixels to the GraphicsPath. The X and Y positions of the circle are offset by a value of 2 pixels so that it is placed midway between the end of one line segment and the start of the next.
You can change the circles into squares by replacing each AddEllipse line with this:
MyPath.AddRectangle(New Rectangle(XPosEnd - 2, YPosEnd - 2, 4, 4))
Grid Lines
Although by definition a grid comprises both horizontal and vertical lines, I have chosen to create two separate procedures, one for the horizontal grid lines and the other for the verticals. You can then decide if you want both sets of gridlines to be used. In a very small PictureBox, having both sets of lines can be distracting and you might want to give the user the choice of horizontal only, vertical only, both or none.
Horizontal Grid Lines
Based on what we have covered so far in this article, I hope you will find the following procedure, which draws a set of horizontal grid lines, fairly easy to follow:
Private Sub DrawHorizontalLines()
' Calculate vertically equal gaps
Dim VertGap As Integer = CInt(VertLineLength / 10)
' Set the Start and End points
' = Left and right margins on the baseline
Dim StartPoint As New Point(LeftMargin + 3, PBLineChart.Height - BaseMargin)
Dim EndPoint As New Point(PBLineChart.Width, PBLineChart.Height - BaseMargin)
' Initial settings
Dim LineStart As New Point(StartPoint.X, StartPoint.Y - VertGap)
Dim LineEnd As New Point(EndPoint.X, StartPoint.Y - VertGap)
Dim ThinPen As New Pen(Color.LightGray, 1)
For i As Integer = 1 To 10
' Draw a line
g.DrawLine(ThinPen, LineStart, LineEnd)
' Reset Start and End Y positions, moving up the vertical line
LineStart.Y -= VertGap
LineEnd.Y -= VertGap
Next
ThinPen.Dispose()
End Sub
The procedure is very similar to that which we used to create the tickmarks on the vertical axis. In fact, as you can probably tell, I've simply recycled that code, changing the start and end points, omitting the text and using a thinner pen in a light color.
Vertical Grid Lines
In the same way, I have used the code that draws the Month names and tweaked it so that it draws vertical lines all the way across the chart:
Private Sub DrawVerticalGridLines()
Dim ThinPen As New Pen(Color.Bisque, 1)
' Calculate length of baseline drawn by the code above
BaseLineLength = PBLineChart.Width - (LeftMargin + RightMargin)
' Calculate the width of each line segment
LineWidth = (BaseLineLength / Sales.Length)
' Set the start point of the first string
Dim LineStartX As Integer = CInt(LeftMargin + 30)
For i As Integer = 0 To Months.Length - 1
g.DrawLine(ThinPen, LineStartX, TopMargin, LineStartX, PBLineChart.Height - (BaseMargin + 4))
' Move start point along to the right
LineStartX += CInt(LineWidth)
Next
ThinPen.Dispose()
End Sub
The only substantial difference being the replacement of DrawString with DrawLine, assigned with appropriate parameter values.
Final Display
Add a call to these two procedures to the button click event.
Private Sub btnDraw_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDraw.Click
DrawOutline()
DrawHorizontalLines()
DrawVerticalGridLines()
DrawTheLine()
ShowMonths()
FinalDisplay()
End Sub
Note that the gridlines are drawn before the actual Sales figures line is drawn. This is important, otherwise the gridlines will tend to erase the sales line as it passes over it and makes it look unfinished.
But finished we are! A fairly basic but usable Line Chart, which you can alter or enhance in many ways as needed.
