Techniques - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Techniques arrow A Tale of a .Net Component (Part III)
A Tale of a .Net Component (Part III)
By DevSource

Rate This Article: Add This Article To:

In the third installment of this series, Peter continues developing his component by adding properties that will help keep track of the minimum and maximum values of the chart.

This is the third article in a series that covers all the details of developing a .Net component. By following this process from start to finish, you may learn aspects of .NET development that are not provided in more specialized articles.

ADVERTISEMENT

In the previous article I promised that this in this installment I would be drawing the legend and axis labels and testing the control. It seems I was getting a bit ahead of myself—there are a few more matters that need attention before we get to those. This is the seemingly minor but actually very important way in which the chart scales its axes to accommodate the minimum and maximum X and Y values.

More on the XYTrace Class

The previous article explained how and why I decided that each trace, or data series, on the chart would be represented by an instance of the XYTrace class, with all the traces contained in a strongly typed collection. You saw how each XYTrace class contained a dynamic array of type PointF to hold the actual data and properties for the trace name, plot color, and a visible flag. This class needs a few more members before it is ready to use, and I decided to complete it before continuing with the XY chart component itself.

An XY chart needs to know the minimum and maximum X and Y values that it will display—in other words, what ranges of X and Y need to be encompassed by the chart axes? One approach is to scan through the data and determine what precisely these values are, and then scale the X and Y axes to exactly these values. As I'll get to in a moment, this is not always the best approach, but you should have it available as an option. Because the data are represented in the XYTrace class, it makes sense that this class should have the ability to report on the maximum and minimum values of X and Y for the data it contains.

To do so, I implemented 4 read-only properties called MaxX, MinX, MaxY, and MinY—the names should be self-explanatory. These are different from ordinary properties in that the value is not held in the class but rather is determined by code when the property is accessed. This is another advantage of property procedures over public variables—they are not limited to returning an existing value but can run code to find or calculate the property value.

For MaxX, the first step is to create a variable and set it to the smallest possible value for the data type, which you will remember is Single for the data to be plotted. To do this I took advantage of the data type's ability to report its own minimum possible value (and maximum too, as we'll see soon):

Dim max As Single = Single.MinValue

Why do I do this? By setting max to the smallest possible value, any actual data value will be larger than its initial value. Then, loop through all the data, which is held in the array pXYValues, and compare each X value with max, setting the maximum equal to any value that is larger than its current value:


Dim i As Integer
For i = 0 To pXYValues.GetUpperBound(0)
   If pXYValues(i).X > max Then max = pXYValues(i).X
Next

Finally, return the final value of max:

Return max

The property procedures for these four properties are shown here. Place this code in the XYTrace.vb module following the private variable declarations that you added from the previous article.:

ReadOnly Property MaxX() As Single
    Get
         Dim max As Single = Single.MinValue
         Dim i As Integer
         For i = 0 To pXYValues.GetUpperBound(0)
             If pXYValues(i).X > max Then max = pXYValues(i).X
         Next
     Return max
    End Get
End Property
ReadOnly Property MaxY() As Single
    Get
        Dim max As Single = Single.MinValue
        Dim i As Integer
        For i = 0 To pXYValues.GetUpperBound(0)
            If pXYValues(i).Y > max Then max = pXYValues(i).Y
        Next
        Return max
    End Get
End Property
ReadOnly Property MinX() As Single
    Get
        Dim min As Single = Single.MaxValue
        Dim i As Integer
        For i = 0 To pXYValues.GetUpperBound(0)
            If pXYValues(i).X < min Then min = pXYValues(i).X
        Next
        Return min
    End Get
End Property
ReadOnly Property MinY() As Single
    Get
        Dim min As Single = Single.MaxValue
        Dim i As Integer
        For i = 0 To pXYValues.GetUpperBound(0)
            If pXYValues(i).Y < min Then min = pXYValues(i).Y
        Next
        Return min
    End Get
End Property

Dealing with Data Minimums and Maximums in the Chart Class

Now that we have added members to the XYTrace class to get the actual minimum and maximum for both X and Y, it's time to turn to the chart itself and determine how it will handle scaling the chart axes. Setting this scale to the precise minimum and maximum values may be appropriate for some situation, but it is not for others. For example:

  • If the axis scales are set to the precise minimum and maximum values, then the traces will touch the axes on all 4 sides. This may be okay, or it may not be.

  • Some charts are easier to read if the minimum Y axis value is 0 even if the minimum data value is not, because the relative differences between the multiple traces are easier to interpret.

  • Even if you use the automatic calculation of the maximum Y value, you may want the Y axis maximum to fall on an even boundary for ease of reading the chart. For example, if the maximum Y value is 27.8 you might want the maximum Y axis value to be 30.

To take these considerations into account, I decided that the XY chart should have the following capabilities:

  • To turn off automatic detection individually for all four quantities (maximum and minimum for both X and Y) and to manually set each axis limit.

  • To specify that the Y maximum, when automatically detected, would be adjusted up to the next multiple of a specified value. For example, if you specify a multiple of 5, then a Y maximum of 27.45 will be adjusted to 30, 3.5 will be adjusted to 5, and so on.

To implement these features I needed nine properties: four Boolean values that would indicate whether the corresponding maximum or minimum is determined automatically, four Single values that would hold the values (either determined internally or set from outside), and finally an Integer that would determine the multiple to which the maximum Y value is rounded to. The code for these properties is shown here. By now you know where to put this code in the ctlXY_Chart module.

' Default values for data ranges min/max if auto not used.
Private pMax_X As Double = 100
Private pMax_Y As Double = 100
Private pMin_X As Double = 0
Private pMin_Y As Double = 0
' If true, calculate X and Y max/min values from data.
' If false, use the corresponding properties.
Private pAutoX_Max As Boolean = True
Private pAutoY_Max As Boolean = True
Private pAutoX_Min As Boolean = True
Private pAutoY_Min As Boolean = True
' If non-zero, MaxY is adjusted to fall on the 
' next multiple of this value. 
Private pMaxY_Multiple As Integer = 0

Property MaxY_Multiple() As Integer
    Get
        Return pMaxY_Multiple
    End Get
    Set(ByVal value As Integer)
        pMaxY_Multiple = value
    End Set
End Property

Property AutoX_Max() As Boolean
    Get
        Return pAutoX_Max
    End Get
    Set(ByVal value As Boolean)
        pAutoX_Max = value
    End Set
End Property

Property AutoY_Max() As Boolean
    Get
        Return pAutoY_Max
    End Get
    Set(ByVal value As Boolean)
        pAutoY_Max = value
    End Set
End Property

Property AutoX_Min() As Boolean
    Get
        Return pAutoX_Min
    End Get
    Set(ByVal value As Boolean)
        pAutoX_Min = value
    End Set
End Property

Property AutoY_Min() As Boolean
    Get
        Return pAutoY_Min
    End Get
    Set(ByVal value As Boolean)
        pAutoY_Min = value
    End Set
End Property

Property Max_X() As Double
    Get
        Return pMax_X
    End Get
    Set(ByVal value As Double)
        pMax_X = value
        pAutoX_Max = False
    End Set
End Property

Property Max_Y() As Double
    Get
        Return pMax_Y
    End Get
    Set(ByVal value As Double)
        pMax_Y = value
        pAutoY_Max = False
    End Set
End Property

Property Min_X() As Double
    Get
        Return pMin_X
    End Get
    Set(ByVal value As Double)
        pMin_X = value
        pAutoX_Min = False
    End Set
End Property

Property Min_Y() As Double
    Get
        Return pMin_Y
    End Get
    Set(ByVal value As Double)
        pMin_Y = value
        pAutoY_Min = False
    End Set
End Property

Note that all of these properties have default values. This is usually a good idea so your component behaves in a predictable way even if the user does not explicitly save those properties. Note also that if any of the 4 max/min properties are explicitly set, the corresponding Auto property is set to False. After all, if the user explicitly sets a value for, say, MaxY, they surely do not want it determined automatically.

Stay Tuned...

We now have even more of the foundation of the XY chart component in place. It may not seem obvious to you yet, but many of the design decisions that have been made so far will have an impact on what happens next. If I have been careful in my design and implementation, that impact will be marked by the absence of problems.




Discuss A Tale of a .Net Component (Part III)
 
>>> Be the FIRST to comment on this article!
 

 
 
>>> More Techniques Articles          >>> More By DevSource
 



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.