.NET Images - Creating a Graph from a Database (Example) 04 April 2004 at 00:00
Creating images using C# is fairly straight-forward. The example I created is a an application which grabs browser statistic information from a table in an Access database and generates a graph from that which it saves in JPG, BMP or GIF format. This isn't a real-world application so there are numerous ways this could be changed to make it more generic.

The Statistics table is structured as follows:

HitID AutoNumber
Browser Text (150)
Operating System Text (150)

The image that was generated from dummy data inserted in the table:

This image is as a result of three classes:

  • BrowserGraph (base class)
  • DataHandler
  • ImageWriter

BrowserGraph.cs

  using System;
  using System.IO;

  namespace BrowserGraph {

    /// 
    /// Base class for creating a new browser statistics graph
    /// 
    public class BrowserGraph {

      /// 
      /// Constructor for BrowserGraph class
      /// Type of image to be generated where 0=JPG; 1=GIF; 2=BMP (other values 
      /// passed will result in a default JPG image being produced)
      /// 
      /// 
      /// 
      public BrowserGraph(int imgType) {
        DataHandler dataHandle = new DataHandler("DSN=BrowserGraph");
        ImageWriter imageWriter = new ImageWriter(dataHandle.GetBrowserData(), imgType);
        dataHandle.CloseConnection();
      }
  			
      /// 
      /// Entry point for class from console
      /// Type of image to be generated where 0=JPG; 1=GIF; 2=BMP (other values 
      /// passed will result in a default JPG image being produced)
      /// 
      public static void Main(string[] args) {
        if (args.Length == 0) {
          new BrowserGraph(0);
        }
        else {
          try {
            new BrowserGraph(Int32.Parse(args[0]));
          }
          catch(System.Exception) {
            new BrowserGraph(0);
          }
        }
      }
    } 
  }

DataHandler.cs

  using System;
  using System.Data;
  using System.Data.ADO;
  using System.Threading;

  namespace BrowserGraph {
    	
    /// 
    /// DataHandler class used by the BrowserGraph class to retrieve data necessary to 
    /// create the statistics graph.
    /// 
    /// 
    /// 
      public class DataHandler {

        ADOConnection connADO = null;	// connection object
    		
        /// 
        /// Constructor for DataHandler class - call CloseConnection() when you are finished with this
        /// class to close the connection to the database
        /// Connection string to be used to connect to the database
        /// 
        public DataHandler(String strConn) {
          // attempt to open a connection to the database, if fail exit
          try {
            connADO = new ADOConnection(strConn);
            connADO.Open();
          }
          catch (System.ArgumentException) {
            Console.WriteLine("Could not open connection - exitting.");
            Environment.Exit(0);
          }
          Console.WriteLine("Connection opened.");
        }
    		
        /// 
        /// Method that must be called to close the connection to the database.
        /// 
        public void CloseConnection() {
          // creates a new thread which attempts to close the connection - cannot be 
          // certain that the connection is not busy
          Thread t = new Thread(new ThreadStart(CloseConnectionThread));
          t.Start();
        } 
    		
        /// 
        /// Private method which actually closes the connection. The thread keeps running until 
        /// the connection to the database is successfully closed or until too many attempts have 
        /// been made (after which an error is written to the console)
        /// 
        private void CloseConnectionThread() {
          Boolean closed = false;
          String delim = "
--------------------------------------------------------------
";
          int i = 0;
          // run the thread
          while(!closed) {
            try {
              // attemps to close the connection -  if successful the thread ends
              connADO.Close();
              Console.WriteLine("Connection closed.");
              closed = true;
            }
            catch (System.Exception se) {
              // unable to close the connection, increment counter and stay in thread
              i++;
              if (i > 1000) {
                closed = true;
                Console.WriteLine("
Failed to close connection after 1000 attempts." + delim + se.Message + delim);
              }
            }
          }
        }
    		
        /// 
        /// Method used to retrieve browser statistics
        /// ADODataReader holding the relevant browser statistic information in the format of 
        /// a ADODataReader with 2 columns - the actual browser string and the number of hits for that browser
        /// 
        /// 
        public ADODataReader GetBrowserData() {
          // build the sql string
          String sql = "SELECT Browser, COUNT(Browser) As Cnt FROM Statistics GROUP BY Browser";
          // create ADO DataReader and command objects
          ADODataReader reader = null;
          // execute the sql query and populate the reader
          ADOCommand adoCmd = new ADOCommand(sql, connADO);
          adoCmd.Execute(out reader);
          return reader;
       }
    }
  }

ImageWriter.cs

  using System;
  using System.Data;
  using System.Data.ADO;
  using System.WinForms;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.Collections;

  namespace BrowserGraph {

    /// 
    /// Class used to write the browser statistics image to disk
    /// 
    /// 
    /// 
    public class ImageWriter {

      private const int COL_WIDTH = 31;		// column width of data fields
      private const int ROW_HEIGHT = 12;		// row height between column values written to graph
      private int imageType = 0;				// type of image (0=JPG (default); 1=GIF; 2=BMP)
      private int currColor = -1;				// current colour for a field
      private Color[] arrColors = { Color.Navy, Color.DarkGreen, Color.Brown, Color.DarkSlateGray, Color.Black };
  		
      /// 
      /// Constructor for ImageWriter class
      /// ADODataReader containing the information to be graphed
      /// Image type to be created (0=JPG(default); 1=GIF; 2=BMP)
      /// 
      public ImageWriter(ADODataReader reader, int imgType) {
        // loop through the returned contents and write to the console
        long maxValue = 0;
        imageType = imgType;
        // put the contents of the reader into a hashtable and close the reader
        Hashtable hash = new Hashtable();
        while (reader.Read()) {
          long tmp = reader.GetInt32(1);
          if (tmp > maxValue) {
            maxValue = tmp;
          }
          hash.Add(reader["Browser"].ToString(), tmp);
        }
        reader.Close();
        CreateImage(hash, maxValue);
      }
    		
      /// 
      /// Private method to create the image 
      /// Hashtable containing the information to be graphed
      /// Highest value to be graphed
      /// 
      private void CreateImage(Hashtable hash, long maxValue) {
        int fields = hash.Count;
        int width = 100 + (fields * COL_WIDTH);
        int height = 185 + (fields * ROW_HEIGHT);
        long heightBase = (165 / maxValue);
        int x = COL_WIDTH + 1;
        int y = 175;
        // create new image and graphics object
        Bitmap b = new Bitmap(width, height);
        Graphics g = Graphics.FromImage(b);
        g.Clear(Color.White);
        // draw base graph template
        g.DrawLine(new Pen(Color.Black), new Point(30,height + 10), new Point(30,15));
        g.DrawLine(new Pen(Color.Black), new Point(5, 180), new Point(width - 5, 180));
        g.DrawString("0", new Font("Verdana", 10), new SolidBrush(Color.Black), new Point(5,180));
        g.DrawString(maxValue.ToString(), new Font("Verdana", 10), new SolidBrush(Color.Black), new Point(5,12));
        // draw individual blocks
        IDictionaryEnumerator ienum = hash.GetEnumerator(); 
        Rectangle rect;
        // loop through enumeration and draw the columns onto the canvas and the browser strings in 
        // line with columns below the graph
        while (ienum.MoveNext()) {
          int h = (int)((long)ienum.Value * heightBase);
          rect = new Rectangle(x, (170 - h) + 10, 30, h);
          g.DrawRectangle(new Pen(Color.Black), rect);
          g.FillRectangle(new SolidBrush(getCurrentColor()), rect);
          g.DrawString(ienum.Value.ToString(), new Font("Arial", 7), new SolidBrush(Color.Black), new Point(x + 1, 170 - h));
          g.DrawString(ienum.Key.ToString(), new Font("Arial", 7), new SolidBrush(Color.Black), new Point(x + 1, y + 10));
          y += ROW_HEIGHT;
          x += COL_WIDTH;	
        }
        // create the image depending on what image type was selected
        switch (imageType) {
          case 0:
            b.Save("matt.jpg", ImageFormat.JPEG);
            break;
          case 1:
            b.Save("matt.gif", ImageFormat.GIF);
            break;
          case 2:
            b.Save("matt.bmp");
            break;
          default:
            b.Save("matt.jpg", ImageFormat.JPEG);
        }
      }
    		
      /// 
      /// Private method to retrieve the color to be used for the current column
      /// Color object contained in the arrColors array
      /// 
      private Color getCurrentColor() {
        currColor++;
        if (currColor >= arrColors.Length) {
          currColor = 0;
        }
        return arrColors[currColor];
      }
    }
  }

The command line entry to compile this project (with docs):

  csc *.cs /r:System.Data.dll;System.Drawing.dll;System.WinForms.dll;System.dll /doc:BrowserGraphDocs.xml
Description and Symbols used for Flowcharts 04 April 2004 at 00:00
The terminator symbol marks the starting or ending point of the system. It usually contains the word "Start" or "End."
A box can represent a single step ("add two cups of flour"), or and entire sub-process ("make bread") within a larger process.
A printed document or report.
A decision or branching point. Lines representing different decisions emerge from different points of the diamond.
Represents material or information entering or leaving the system, such as customer order (input) or a product (output).
Indicates that the flow continues on another page, where a matching symbol (containing the same letter) has been placed.
Lines indicate the sequence of steps and the direction of flow.