Recipe 9.23. Drawing a Simple Chart


Problem

You want to create your own data charts, and you would like to have code for a sample chart as a starting point for your own customizations.

Solution

Sample code folder: Chapter 09 \DrawingCharts

The simple chart presented in this recipe should provide plenty of creative ideas and useful techniques for designing your own custom charts.

Discussion

The chart presented here provides a good starting point for drawing your own charts, but it shouldn't be used as presented. For one thing, the data values are hard-coded into an array in the form's Paint event, and you'll likely want to pass in your own data for plotting. The goal of this example is to present several graphics techniques in an easy-to-follow way.

As in most of the graphics examples in this chapter, the drawing takes place in the form's Paint event. The graphics drawing surface is referenced for easy use of its drawing methods:

 Private Sub Form1_Paint(ByVal sender As Object, _       ByVal e As System.Windows.Forms.PaintEventArgs) _       Handles Me.Paint    ' ----- Draw a nice chart.     Dim canvas As Graphics = e.Graphics 

For demonstration purposes, an array of Y data point values is hardcoded in this routine, and the corresponding X values are assumed to be evenly spaced 10 units apart in the range 0 to 100:

    ' ----- Create an array of data points to plot.    Dim chartData() As Single = _       {20, 33, 44, 25, 17, 24, 63, 75, 54, 33} 

We'll use three pens: a red one, a black one, and a gray one. By setting each pen's widths to 1, we guarantee the sketched lines to be one pixel wide even if the scaling changes, and in this example we do change the scaling to plot the entire chart on the form no matter what size the window is stretched to:

    ' ----- Create some pens.    Dim penRed As New Pen(Color.Red, -1)    Dim penBlack As New Pen(Color.Black, -1)    Dim penShadow As New Pen(Color.Gray, -1) 

The next lines create the font and brush used to draw the axis numbers along the tick marks. The font size is relative to the chart scaling, which means that as the chart window is resized, the numbers along the axis will grow and shrink proportionately:

    ' ----- Prepare to add labels.    Dim labelFont As New Font("Arial", 3, FontStyle.Regular)    Dim labelBrush As New SolidBrush(Color.Blue) 

Several variables are used during the scaling process and to plot the data points:

    ' ----- Used to plot the various elements.    Dim x1, y1 As Single 'Lower left corner    Dim x2, y2 As Single 'Upper right corner    Dim scaleX, scaleY As Single    Dim xScan, yScan As Single    Dim oneBar As RectangleF 

The chart is drawn in a rectangle from 0 to 100 in both the X and Y directions. By scaling the graphics surface from 10 to 110, a margin is left for the axis labels. By default, the Y scaling of a graphics surface starts at the top-left corner and increases as you move down in the area. A standard X-Y chart assumes an origin in the bot-tom-left corner, with increasing values going up the graphics surface. This requires the Y scaling factor in the ScaleTransform() method to be a negative value, which inverts the scale. Also, once inverted, the scale origin needs to be shifted, or trans-lated, appropriately to relocate the origin to the bottom left of the graphics surface. This is accomplished using the Graphics object's TranslateTransform() method:

    ' ----- Set the scaling.    x1 = -10    y1 = -10    x2 = 110    y2 = 110    scaleX = Me.ClientSize.Width / (x2 - x1)    scaleY = Me.ClientSize.Height / (y2 - y1)    canvas.ScaleTransform(scaleX, -scaleY) '(inverted)    canvas.TranslateTransform(-x1, -y2) '(inverted) 

The chart's background color, outline, and gridlines are drawn in the following lines of code:

    ' ----- Color the background.    canvas.Clear(Color.Cornsilk)    ' ----- Draw chart outline rectangle.    canvas.DrawRectangle(penBlack, New Rectangle(0, 0, 100, 100))    ' ----- Draw the chart grid.    For xScan = 10 To 90 Step 10       canvas.DrawLine(penBlack, xScan, 0, xScan, 100)    Next xScan    For yScan = 10 To 90 Step 10       canvas.DrawLine(penBlack, 0, yScan, 100, yScan)    Next yScan 

We'll use a 3D shadowed effect to draw the vertical data bars. First, draw each bar using a transparent shade of gray. To create the transparent gray color, set the alpha component of the solid brush's color to 127. As you can see in Figure 9-33, the gridlines show through the transparent "shadows" created by these rectangles.

Figure 9-33. A simple chart that can be used as a starting point for customizing your own special-purpose charts


The data bar rectangles (they're actually red) are then drawn on top of and slightly above and to the right of the transparent gray bars. This results in a nice 3D shadowed effect:

    ' ----- Draw some shadowed bars.    For xScan = 0 To 90 Step 10       ' ----- Draw the shadow first.       oneBar.X = xScan + 0.6       oneBar.Y = 0       oneBar.Width = 6       oneBar.Height = chartData(xScan \ 10) - 2       canvas.FillRectangle(New SolidBrush(Color.FromArgb(127, _          Color.Gray)), oneBar)       ' ----- Now draw the bars in front.       oneBar.X = xScan + 2       oneBar.Y = 0       oneBar.Height = chartData(xScan \ 10)       canvas.FillRectangle(New SolidBrush(Color.Red), oneBar)    Next xScan 

When drawing text, a complication arises if the scaling has been inverted: the text is drawn upside down! This might be useful in some situations, but to get the labels correct on this chart, the Y scaling transform must be reinverted to correctly plot the tick-mark numbers:

    ' ----- Need to un-invert the scaling so text labels are    '       right-side-up.    canvas.ResetTransform()    canvas.ScaleTransform(ScaleX, ScaleY)    canvas.TranslateTransform(-x1, -y1) 

Each number along the X and Y axes is drawn using the Graphics object's DrawString() method. Parameters passed to this method include the string to draw, the font for the text, the brush for the text's color, and the coordinates at which to start drawing the string. These coordinates are not pixel locations, because the graphics have been scaled using transforms. Instead, they are relative positions or units within the scaled world. This causes the text to be plotted in the correct relative position, no matter what size the window is stretched to:

    ' ----- Label the Y-axis.    For yScan = 0 To 100 Step 10       canvas.DrawString(yScan.ToString, labelFont, labelBrush, _          -2 * yScan.ToString.Length - 3, 97 - yScan)    Next yScan    ' ----- Label the X-axis.    For xScan = 0 To 100 Step 10       canvas.DrawString(xScan.ToString, labelFont, labelBrush, _          xScan + 1.7 - 2 * xScan.ToString.Length, 103)    Next xScan 

The last step is to clean up all of the graphics objects we've created:

    ' ----- Clean up.    labelFont.Dispose()    labelBrush.Dispose()    penRed.Dispose()    penBlack.Dispose()    penShadow.Dispose()    canvas = Nothing End Sub 

Figure 9-33 shows the chart drawn on the form as a result of the previous code. Set-ting the form's DoubleBuffered property to true ensures that the chart is drawn smoothly and continuously as the form is resized when the following code is included:

 Private Sub Form1_Resize(ByVal sender As Object, _       ByVal e As System.EventArgs) Handles Me.Resize    ' ----- Refresh on resize.    Me.Refresh() End Sub 




Visual Basic 2005 Cookbook(c) Solutions for VB 2005 Programmers
Visual Basic 2005 Cookbook: Solutions for VB 2005 Programmers (Cookbooks (OReilly))
ISBN: 0596101775
EAN: 2147483647
Year: 2006
Pages: 400

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net