Techniques - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Techniques arrow Page 2 - Tale of a .NET Component (Part IV)
Tale of a .NET Component (Part IV)
By Peter Aitken

Rate This Article: Add This Article To:

Tale of a .NET Component (Part IV) - ' Axis Labels'
( Page 2 of 3 )

The Axis Labels

The axis labels show the numerical values at the tick marks along each axis. These will be generated internally based on the minimum and maximum data values for both the X and the Y axis and the number of tick marks. Thus, no properties are required for these elements.

ADVERTISEMENT

The approach is as follows. We already know, as detailed in the previous article, what the minimum and maximum values of X and Y are. We also know where the top and bottom of the Y axis are, and where the left and right ends of the X axis are (based on the width and height of the component and the MYBOTTOM, MY TOP, MYLEFT, and MYRIGHT constants). As with other parameters, we define constants for the distance between the labels and the axis:

   
Const X_AXIS_LABEL_OFFSET = 5
Const Y_AXIS_LABEL_OFFSET = 5

For the Y axis labels, we need to take the text height into account. This is because we want each label to be centered on its adjacent tick mark rather than below it. Recall that the position of text is defined as the top left corner of the bounding rectangle. Therefore, the position must be "raised" by an amount equal to one-half the text height.

Start by creating strings that contain the labels for the minimum and maximum Y values:


Dim L1 As String = Format(pMin_Y, "G")
Dim L2 As String = Format(pMax_Y, "G")

Next, get the text height. Because both labels will be rendered in the same font, the height of one is the same as the height of the other:


Dim TextHeight As Integer = (e.MeasureString(L1, f).Height) / 2

The Y axis labels will be displayed on the right Y axis, so the horizontal position is equal to the position of the axis plus the label offset: Me.Width - MYRIGHT + Y_AXIS_LABEL_OFFSET. The vertical position is equal to the position of the upper or lower X axis (for yMax and yMin respectively). As already mentioned, the vertical position must be corrected by one-half of the text height. Therefore, here's the code to display the Y axis labels for yMin and yMax:

e.DrawString(L1, f, Brushes.Black, Me.Width - MYRIGHT + _
    Y_AXIS_LABEL_OFFSET, Me.Height - MYBOTTOM - TextHeight)
e.DrawString(L2, f, Brushes.Black, Me.Width - MYRIGHT + _
    Y_AXIS_LABEL_OFFSET, MYTOP - TextHeight)

Displaying the intermediate Y axis values gets a bit more complex. Because the number of tick marks is not fixed (while set by a constant in the code, it could be changed), the number, values, and positions of the labels may change. The approach, then, is to divide the Y axis into a number of intervals based on the number of tick marks, as was done for displaying the tick marks themselves in part I of this series. For example, if there are 11 tick marks specified we need 9 intermediate axis labels evenly distributed between the ones, already displayed, for yMin and yMax.

Time to start coding. First, to get the physical distance between Y axis labels, divide the extent of the Y axis into the required number of intervals:


Dim deltaY As Integer = (Me.Height - MYTOP - MYBOTTOM) / _
  (NUM_Y_TICKS - 1)

The X position will be the same for all Y axis labels. Note that this takes the length of the tick marks into account—in other words, the labels are offset form the right end of the tick marks, not the axis itself:


Dim xPos As Integer = Me.Width - MYRIGHT + ((TICK_MARK_LENGTH / 2 _
    + Y_AXIS_LABEL_OFFSET))

To get the actual Y values that will displayed, the Y range—that is, yMax minus yMin—is divided by (NUM_Y_TICKS – 1) to get the numeric interval between labels. For example, suppose there are 11 Y ticks and yMin and yMax are 50 and 0 respectively. Then

(50 – 0) / (11 – 1) = 50/10 = 5

Thus, the Y axis labels will be (from top to bottom) 50, 45, 40, ... 5, 0. Here's the code that loops to create the intermediate Y axis labels:


For i = 1 To NUM_Y_TICKS - 2
   L1 = Format(pMax_Y - (i * (pMax_Y - pMin_Y) / (NUM_Y_TICKS - 1)), "G")
   e.DrawString(L1, f, Brushes.Black, xPos, MYTOP + _
        (i * deltaY) - TextHeight)
Next

Displaying the X axis labels follows exactly the same logic, so I will not explain the code in detail. There is one difference—because the X axis labels are displayed in a horizontal row, they can tend to crowd each other if displayed at every tick mark. Therefore, the code here displays an X axis label only at alternate tick marks.

The entire DrawAxisLabels() procedure is shown in Listing 1.

Listing 1. DrawAxisLabels() displays the labels on the X and Y axes.


Private Sub DrawAxisLabels(ByVal e As Graphics)

' These are the min and max Y values.
Dim L2 As String = Format(pMax_Y, "G")
Dim L1 As String = Format(pMin_Y, "G")
Dim i As Integer

' Draw labels next to ticks on right vertical axis.
' Get 1/2 of height of text.
Dim TextHeight As Integer = _
    (e.MeasureString(L1, f).Height) / 2
' First the Ymin and Ymax values.
e.DrawString(L1, f, Brushes.Black, _
    Me.Width - MYRIGHT + _
    Y_AXIS_LABEL_OFFSET, _
    Me.Height - MYBOTTOM - TextHeight)
e.DrawString(L2, f, Brushes.Black, Me.Width - _
    MYRIGHT + Y_AXIS_LABEL_OFFSET, _
   MYTOP - TextHeight)
' Now the intermediate values.
' Get height of plot area and divide 
' into (NUM__TICKS - 1) intervals.
Dim deltaY As Integer = _
    (Me.Height - MYTOP - MYBOTTOM) / _
    (NUM_Y_TICKS - 1)
Dim xPos As Integer = Me.Width - MYRIGHT + _
    ((TICK_MARK_LENGTH / 2 + Y_AXIS_LABEL_OFFSET))
For i = 1 To NUM_Y_TICKS - 2
    L1 = Format(pMax_Y - (i * (pMax_Y - pMin_Y) / _
        (NUM_Y_TICKS - 1)), "G")
    e.DrawString(L1, f, Brushes.Black, xPos, _
        MYTOP + (i * deltaY) - TextHeight)
Next

' Draw the X axis labels
Dim yPos As Integer = (Me.Height - MYBOTTOM) + _
    X_AXIS_LABEL_OFFSET
Dim deltaX As Integer = (Me.Width - MYLEFT - MYRIGHT) _
    / (NUM_X_TICKS - 1)
Dim TextWidth As Integer
TextHeight = e.MeasureString("XXX", f).Height
For i = 0 To NUM_X_TICKS - 1 Step 2
    L1 = Format(pMin_X + (i * ((pMax_X - pMin_X) / _
        (NUM_X_TICKS - 1))), "G")
    TextWidth = e.MeasureString(L1, f1).Width / 2
    e.DrawString(L1, f, Brushes.Black, MYLEFT + _
        (deltaX * i) - (TextWidth), yPos)
Next

End Sub



 
 
>>> More Techniques Articles          >>> More By Peter Aitken
 



Microsoft's Future: A Chat With Their CTO, Barry Briggs

Play Video >

All Videos >

Julia explores the Robotics Studio!

Read now >

Messages to Bill Gates!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.