Step 5: Horizontal Axis and the Bars
5a. Base Line
The logic for the Horizontal (X) Axis is similar to that used to draw and measure the Vertical Axis:
' First draw the X (horizontal) axis line
g.DrawLine(LinePen, LeftMargin, PBBarChart.Height - BaseMargin, _
PBBarChart.Width - RightMargin, PBBarChart.Height - BaseMargin)
' Calculate length of baseline drawn by the code above
Dim BaseLineLength As Integer = _
PBBarChart.Width - (LeftMargin + RightMargin)
5b. The Bars
Calculating the width of each bar again is simple math: Divide the length of the baseline by the number of bars (which is the same as the number of elements in the SalesData arraylist – one per country). Subtract the width you decided earlier to insert between bars.
Dim BarWidth As Double = (BaseLineLength / SalesData.Count) - BarGap
Each bar is of course rectangular, so we will create a Rectangle object for this purpose. To locate the X-position of the first bar we start at the intersection of the x and y axis and then move to the right a distance equal to the width we decided to insert between bars. The Y-position is set one pixel above the baseline:
Dim BarRect As Rectangle
Dim BarStartX As Integer = LeftMargin + BarGap
Dim BarStartY As Integer = PBBarChart.Height - (BaseMargin + 1)
Next, create a brush with which to draw and fill the bar rectangles:
Dim BarBrush As New SolidBrush(Color.BurlyWood)
So much for the width. Now we need to consider the heights of the bars. Obviously the height of each bar will represent the sales figure for a country. We have already configured the vertical axis so that it is marked out to a maximum of 1000 units of sales values.
The vertical axis also has another value – the number of pixels that make up its length. In order to ensure that each bar’s height is drawn proportionately to the total pixels available we must calculate the scale, i.e. how many Sales are represented by a single pixel up that vertical line. This measurement will be used to ensure that the bars are drawn proportionately within the overall height available for them.
This may sound complicated. It really isn’t and the calculation itself is very simple:
Dim VertScale As Double
VertScale = VertLineLength / 1000
By enumerating through the information stored in the arraylist, we can pull out the sales figures one after the other. We use this figure to calculate the height of the bar .
There are several ways of approaching the task of drawing the bars in the correct positions, some easier than others. The approach I have taken for this article is as follows. For each bar:-
- Calculate the Bar’s height.
- Create a rectangle to represent the Bar.
- “Pull” the Y-position upwards until it reaches to place where the top left of the rectangular bar should be. Why do we have to do this? Because the rectangle is drawn “outwards and downwards”, not “outwards and upwards”. If we don’t make this change, the bars will be drawn below the baseline, not above it.
- Draw and Fill the Rectangle to create the bar
- Move the X-Position by the calculated amount to the right.
And here is the code which does exactly that:
For Each gd As GraphData In SalesData
' Calculate Bar Height
Dim BarHeight As Integer = CInt(gd.Sales * VertScale)
' Create a rectangle for the Bar
BarRect = New Rectangle(BarStartX, BarStartY, CInt(BarWidth), _
' Pull the Y point upwards so that the bar (rectangle) will
' stretch back down to the baseline when drawn
' Fill the Bar
' Optionally draw a line round the bar
' Increase the X value by bar width plus gap
' ready for next bar to be drawn.
BarStartX += CInt(BarWidth + BarGap)
5c. Country Names
The names of the countries are displayed at the bottom of each bar. In a later article we will investigate more challenging and pictorial ways of doing this but for now simple text using DrawString will suffice.
As the process is similar to those we have used already in this project, the commented code will, I hope, make sense to you at this stage:
' Set the start point of the first string
Dim TextStartX As Integer = LeftMargin + BarGap + 4
' Create a Brush to draw the text
Dim TextBrsh As Brush = New SolidBrush(Color.Black)
' Create a Font object instance for text display
Dim TextFont As New Font("Arial", 11, FontStyle.Bold)
For Each gd As GraphData In SalesData
' Draw the name of the country
g.DrawString(gd.Country, TextFont, TextBrsh, TextStartX, _
CInt(PBBarChart.Height - (BaseMargin - 4)))
' Move start point along to the right
TextStartX += CInt(BarWidth + BarGap)