Visual Studio 2010!

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.
ADVERTISEMENT
ADVERTISEMENT

 

DevSource.com: Your Source for Visual Studio on Facebook
ADVERTISEMENT
Tale of a .NET Component (Part V)
By Peter Aitken

Rate This Article: Add This Article To:

Tale of a .NET Component (Part V)
( Page 1 of 2 )

Peter Aitken wraps up his detailed saga of developing an XY chart component from scratch.

This is the fifth and final 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.

The earlier articles have dealt with drawing the chart axes and tick marks, with the data representation in the XYChart class, with the problem of determining and using the minimum and maximum values for the X and Y data, and with displaying the axis titles, labels, and the legend. In this final article we will finally get to what may be considered the heart of the component, the data plots themselves.

The Paint Procedure

In a previous article we saw how a component's Paint event procedure is called to render the component's visual interface to the screen. This call happens automatically. Thus, the code for component display must go here.

For the XY chart component, the axes and tick marks can be rendered under all circumstances. Other elements, however, should be rendered only of the component has data to plot. Recall that the data for each individual plot is in an XYTrace object, and that all of these XYTrace objects are maintained in the component's Traces collection. Thus, we can render the other chart elements only if Traces.Count is > 0, indicating that the component has data. Here's the code in the Paint event procedure:

DrawBoxAndTickMarks(e.Graphics)
If Traces.Count > 0 Then
   DrawAxisTitles(e.Graphics)
   DrawLegend(e.Graphics)
   DrawAxisLabels(e.Graphics)
   DrawPlots(e.Graphics)
End If

With the exception of DrawPlots, all of the procedures called here have been covered in earlier articles. We'll explore DrawPlots() here.

Scaling the Chart Area

In article 3 we explored the way in which the component could automatically calculated maximum and minimum values for X and Y, or the used could manually specify these individual values. You can refer back to that article if you wish, but the bottom line is that the maximum and minimum values that the plotting routine will use are in the variables xMin, xMax, yMin, and yMax. These are the values that will define the full extent of the chart axes.

When it comes time to plot, we are faced with the fact that the data are in the original units but the plot area on-screen uses pixel coordinates. What's more, the screen coordinates by default have larger Y values further down, and of course we want to plot the data with larger Y values upward.

There are two ways to approach this problem. One would be to transform the data to pixel coordinates. While this idea is perfectly feasible, I decided to take the opposite approach and transform the screen coordinates to match the data. I knew this was possible because of my familiarity with the Graphics class and particularly its ScaleTransform and TranslateTransform methods. Let's look at these.

Transform Methods

The Graphics class provides for two types of geometric transforms that we need for this project, a scale transform and a translate transform.

A scale transform multiplies each X and Y value by a scaling factor. This has the effect of stretching or shrinking the image as well as moving it. Figure 1 shows an example in which X has been scaled by 3 and Y has been scaled by 2.

Suppose you draw a box with the top left corner at (1,1) and width and height of 1. Unscaled, this would be displayed as the dashed box in the figure. With the specified scaling applied, however, both the position and the size of the box are changed, as shown by the solid box.

The second type of transform is the translate transform. It works by adding a specified factor to all the X and Y values, with the effect of moving the image without changing its size or shape.

Back to the Code

Now that you have seen the transforms that we will use, let's figure out what exactly we need to do.

  1. Change the scaling of the plot so that the distance between the left and right Y axes is represented by the range of X values, that is xMax – xMin.
  2. Change the scaling of the plot so that the distance between the upper and lower X axes is represented by the range of Y values, that is yMax – yMin.
  3. Flip the Y coordinates so that larger values are toward the top.
  4. Translate the plot area so that the point at the lower left corner of the plot area is represented by the coordinates (xMin, yMin).

The first step is to calculate scaling factors for both X and Y. For X, this is done by taking the width of the plot area in pixels and dividing it by the range of X values. The same approach is used for Y except that the value is made negative, which flips the Y axis so that positive is upward. Then, apply the transform (recall that g is the Graphics object that will be used to draw the plots):

Dim scaleX As Single = (Me.Width - MYLEFT - MYRIGHT) / (xMax - xMin)
Dim scaleY As Single = -(Me.Height - MYTOP - MYBOTTOM) / (yMax - yMin)
g.ScaleTransform(scaleX, scaleY)

Next is the translate transform. Note that these calculations take into account the scaling factors that have already been applied:

Dim transX As Single = MYLEFT - (xMin * scaleX)
Dim transY As Single = ((Me.Height - MYBOTTOM) - (scaleY * yMin))
g.TranslateTransform(transX, transY, MatrixOrder.Append

At this point the plot area is ready—that is, it has been transformed so that we can plot the raw data values and the plots will be in the proper positions.



 
 
>>> More Microsoft Architecture Articles          >>> More By Peter Aitken