Languages - DevSource
DevSource: Microsoft Developer Resource DevSource Home Sponsored by Microsoft Home Add Ons Architecture Languages Techniques Using VS Forums
Home arrow Languages arrow Data Visualization in C# with GDI+
Data Visualization in C# with GDI+
By Rick Leinecker

Rate This Article: Add This Article To:

Data Visualization in C# with GDI+
( Page 1 of 3 )

I've written quite a few programs that show various forms of data. Much of the data that many of my programs display is statistical. If the data is a bunch of numbers, management goes glassy-eyed when they look at it. They're used to bar charts and other graphs. As with most non-technical people, they need visuals.

I remember a time when I was demonstrating an application that gathered a wealth of web usage statistics. I went the extra mile to do a good job. I was ready for the applause. But all I got were blank stares. In spite of my hard work, the text-based numbers didn't impress anyone. I came back the next week with the same exact reports in a graphical format and they were ecstatic.

ADVERTISEMENT

This article talks about using GDI+ to add visuals that bring statistical data alive. I realize that you can use Crystal Reports to generate and display reports. Think of this as the lite version. You don't need to learn Crystal or worry about dependencies. It's simplicity instead of the immense power of Crystal.

We'll go through and generate three types of visual displays: a bar graph, a pie chart, and a line graph. I'll teach you how the GDI+ commands work so that you can easily change things around to suite your needs.

Click here to read how to get started with GDI+ programming in C#.

I wrote a demo application that displays data as a bar graph, a pie chart, or a line graph. You can download it here.

Bar Graphs

We'll start off by representing data as a bar graph. Each data item is drawn with a filled rectangle. (I'll talk about how the data is generated and normalized later in this article.) There are four overrides for the Graphics.FillRectangle method. All four overrides provide GDI+ with a Brush object, the upper left coordinate of the rectangle, and the width and height of the rectangle. How these overrides differ is in the way the information is given to the method. (Once again, check out the link above as the referenced article explains this.)

I usually find myself using the following override:

Graphics.Rectangle(objSolidBrush, x, y, width, height);

To represent data, though, you would want to loop through and deal with each data element. The height of the drawn rectangle would depend on the data value. The x coordinate of the drawn rectangle would depend on the iteration number of the loop.

First, let's talk about the height of the rectangle. Let's say our data has values from 0 to 100. Then let's say that the height of the window to which we'll draw has a height of 200 pixels. In order to fill the window, we could calculate the height of the rectangle to draw by multiplying the data by two. But we can't just use zero for the y coordinate with the calculated width. That would create a bar graph where the bars hang from above instead of rise from the base of the window as follows.

This bar graph uses the Y coordinate of 0 and the bars appear to hang. Most bar graphs don't do this.

This bar graph rises from the base of the window and is how most bar graphs appear.

After you calculate the height of the rectangle that will represent a data element, you must then calculate the Y coordinate at which it will be drawn by subtracting the rectangle height from the height of the window as follows.

int nBarHeight = nData * 2;
int nYCoordinate = nHeightOfDrawWindow – nBarHeight;

Now let's talk about calculating the X coordinate and the rectangle width. Calculating the width is easy, simply take the width of the window and divide by the number of data elements. The X coordinate can then be easily calculated by multiplying the loop iteration (such as 0, 1, 2, etc.) by the calculated rectangle width. With that said, though, we don't want to draw the entire width for each rectangle because we'll want some space between the bars. The following code illustrates this.

int BarWidth = nWidthWidth / nNumberOfDataElements;
int nXCoordinate = nLoopCounter * nBarWidth;
Graphics.FillRectangle(objSolidBrush, nXCoordinate, nYCoordinate,
   nBarWidth-6, nBarHeight);

The following code is the complete method from the demonstration program that draws the bar graph.

// This method draws the bar graph elements.
private void DrawBarGraph(Graphics g)
{
     // Get the count into a local variable
     //   so that it's a little easier and the
     //   code is more readable.
     int nCount = m_DataSetToRender.Length;

     // Calculate the bar width.
     int nBarWidth = ( pnlDisplay.Size.Width / nCount );

     // We'll cycle through colors, so we need a variable.
     int nColorIndex = 0;

     // The panel height is used to calculate the
     //   y coordinate for drawing.
     int nPanelHeight = pnlDisplay.Size.Height;

     // Loop through the data.
     for (int i = 0; i < nCount; i++)
     {
          // Draw a filled rectangle.
          g.FillRectangle(new SolidBrush(m_Colors[nColorIndex]),
               i * nBarWidth + 3, nPanelHeight - m_DataSetToRender[i],
               nBarWidth - 6, m_DataSetToRender[i]);

          // Cycle to the next color and wrap around
          //   if we've gotten past the end.
          nColorIndex++;
          if (nColorIndex >= m_Colors.Length)
          {
               nColorIndex = 0;
          }
     }
}

The following figure shows the demonstration program rendering a bar graph.



 
 
>>> More Languages Articles          >>> More By Rick Leinecker
 



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.