Click here to Skip to main content
15,880,392 members
Articles / Desktop Programming / Windows Forms

Write Your Own Bar Chart Winforms User Control

Rate me:
Please Sign up or sign in to vote.
4.92/5 (13 votes)
21 Aug 2007CPOL3 min read 69.6K   1.9K   30   2
An article on how to create a Bar Chart Winforms User control
Image 1

Introduction

I have some free time between projects recently. I thought of utilizing my time in doing something that is useful in my next project which is going to start soon. When I looked at the requirements, I found that there is a bar chart that has to be displayed in the windows .NET application. I have been looking for some ready made controls on the net and found many controls but none of them are free. There are some good graph controls available for buying and they have a lot of other features which I don't need. I don't need many features in the control, so I thought it is a waste of money to recommend purchase of those controls.

Hence I started building my own simple control. I know this is a very basic control and it might not have all the features someone expects, but it serves my purpose. If anyone is interested, she/he can enhance this control for the features she/he wants. This is not a fully tested control.

For this article, I used VS 2003, SQL Server Database. In this project I used the System.Drawing namespace for displaying the UI.

Database Preparation

The database for this project contains one table which is used as data source. I named the database as "ChartDB" and table name is "ControlSource" which has two columns "XValues" and "YValues". This database is a test database which is used to show the Bar chart.

The table structure and data are as follows. (Script for generating table is included in Database folder of the source code.)

ControlSource table structure:

Image 2

Project Structure

Image 3

Using the Code

The main project is "Charting" which is a class library type of project. The core of the project is a Windows User control "BarChart.cs" which creates the bar chart. The "TestBarchart" project is the test project which uses the Barchart user control. First let us discuss about User control.

Add a user control to the Charting project:

Image 4

Then start writing code. The main code snippets are given below:

Declare private variables required:

C#
#region Private Variables
int x_margin=20;
int y_margin=20;
int x_in_margin=20;
int y_in_margin=20;
int font_size;
double y_scale=1;
int n_datpoints =4;
Color primaryLineColor = Color.DeepSkyBlue;
Color alternateLineColor = Color.Red;
Color baselineColor = Color.Black;
Color borderlineColor = Color.Black;
Color primaryFillColor = Color.DeepSkyBlue;
Color alternateFillColor = Color.Red;
Color primaryValueColor = Color.Blue;
Color alternateValueColor = Color.Red;
//Databinding-Start
private DataTable dataSource;
private string dataMember_Y;
private string dataMember_X;
//Databinding-End
#endregion Private Variables

Add the properties required to the control:

C#
  #region Public Properties
  
  public int Xmargin
  {
   get { return this.x_margin; }
   set { this.x_margin = value; }
  }
  
  public int Ymargin
  {
   get { return this.y_margin; }
   set { this.y_margin = value; }
  }
  
  public int Xinmargin
  {
   get { return this.x_in_margin; }
   set { this.x_in_margin = value; }
  }
  public int Yinmargin
  {
   get { return this.y_in_margin; }
   set { this.y_in_margin = value; }
  }
  public Color PrimaryLineColor
  {
   get { return this.primaryLineColor; }
   set { this.primaryLineColor = value; }
  }
  public Color AlternateLineColor
  {
   get { return this.alternateLineColor; }
   set { this.alternateLineColor = value; }
  }
  public Color BaselineColor
  {
   get { return this.baselineColor; }
   set { this.baselineColor = value; }
  }
  public Color BorderlineColor
  {
   get { return this.borderlineColor; }
   set { this.borderlineColor = value; }
  }
  public Color PrimaryFillColor
  {
   get { return this.primaryFillColor; }
   set { this.primaryFillColor = value; }
  }
  public Color AlternateFillColor
  {
   get { return this.alternateFillColor; }
   set { this.alternateFillColor = value; }
  }
  public Color PrimaryValueColor
  {
   get { return this.primaryValueColor; }
   set { this.primaryValueColor = value; }
  }
  public Color AlternateValueColor
  {
   get { return this.alternateValueColor; }
   set { this.alternateValueColor = value; }
  }
  //Data Binding - Start
  [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, 
    System.Design")]
  [Category("Data")]
  [DefaultValue(null)]
  public DataTable DataSource
  {
   get
   {
    return this.dataSource;
   }
   set
   {
    if (this.dataSource != value)
    {
     this.dataSource = value;
    }
   }
  }
  [Category("Data")]
  [Editor("System.Windows.Forms.Design.DataMemberListEditor,System.Design",
     "System.Drawing.Design.UITypeEditor, System.Drawing")]
  [DefaultValue("")]
  public string DataMemberY
  {
   get
   {
    return this.dataMember_Y;
   }
   set
   {
    if (this.dataMember_Y != value)
    {
     this.dataMember_Y = value;
    }
   }
  }
  [Category("Data")]
  [Editor("System.Windows.Forms.Design.DataMemberListEditor,System.Design", 
    "System.Drawing.Design.UITypeEditor, System.Drawing")]
  [DefaultValue("")]
  public string DataMemberX
  {
   get
   {
    return this.dataMember_X;
   }
   set
   {
    if (this.dataMember_X != value)
    {
     this.dataMember_X = value;
    }
   }
  }
  //Data Binding - End

#endregion Public Properties

In the above code snippet, the important thing to understand is the data binding. In the properties, we gave datatable type as datasource property. Hence we can assign any datatable as data source for the control. DataMemberX and DataMemberY are string types where we need to give the column names in table (In the test project, they are "XValues" and "YValues").

DrawBarChart Method

C#
#region DrawBarChart method
private void DrawBarChart(System.Windows.Forms.PaintEventArgs e)
{
 try
 {
  // Create a new pen.
  Pen PrimaryColorPen = new Pen(primaryLineColor);
  Pen AlternateColorPen = new Pen(alternateLineColor);
  Pen BaseLinePen = new Pen(baselineColor);
  Pen BorderLinePen = new Pen(borderlineColor);
  // Create new Solid Brush
  SolidBrush PrimarysolidBrush = new SolidBrush(primaryFillColor);
  SolidBrush AlternatesolidBrush = new SolidBrush(alternateFillColor);
  SolidBrush PrimaryValueBrush = new SolidBrush(primaryValueColor);
  SolidBrush AlternateValueBrush = new SolidBrush(alternateValueColor);

  if (dataSource.Rows.Count!=0)
  {
   n_datpoints = dataSource.Rows.Count;}
  float fbar_space = (float)(this.Width - 2* (x_margin+x_in_margin))
  /(2*n_datpoints);
  int bar_space= (int)Math.Round(fbar_space);
  int bar_width = (int)Math.Floor(fbar_space);
  font_size = (int) this.Font.Size;

  //Calculate the scale
  y_scale = (this.Height - 2*(y_margin + y_in_margin))/
  double.Parse(dataSource.Compute("MAX("+dataMember_Y+")","").ToString());
  //Draw border
  e.Graphics.DrawRectangle(BorderLinePen,
   new Rectangle(x_margin,y_margin,this.Width-2*x_margin,this.Height-2*y_margin));
  for (int i=0;i<n_datpoints;i++)
  {
   if (i%2==0)
   {
    e.Graphics.DrawRectangle(PrimaryColorPen,
     new Rectangle(x_margin+x_in_margin+(i+1)*bar_width+i*bar_space,
  this.Height - y_margin - y_in_margin - (int)(Convert.ToInt32
  (dataSource.Rows[i][dataMember_Y].ToString())* y_scale),bar_width,
  (int)(Convert.ToInt32(dataSource.Rows[i][dataMember_Y].ToString())* y_scale)));

    e.Graphics.FillRectangle(PrimarysolidBrush,x_margin+x_in_margin+(i+1)
  *bar_width+i*bar_space,this.Height - y_margin - y_in_margin - (int)
  (Convert.ToInt32(dataSource.Rows[i][dataMember_Y].ToString())* y_scale),
   bar_width,(int)(Convert.ToInt32
  (dataSource.Rows[i][dataMember_Y].ToString())* y_scale));

   using (Font font = new Font
      (this.Font.Name,font_size,this.Font.Style,this.Font.Unit))
    {
     Point point1 = new Point(x_margin+x_in_margin+(i+1)*bar_width+i*
      bar_space-3,this.Height - y_margin - y_in_margin -
      (int)(Convert.ToInt32
  (dataSource.Rows[i][dataMember_Y].ToString())* y_scale)-(int)font.Size-5);
     Point point2 = new Point(x_margin+x_in_margin+(i+1)*bar_width+i*
      bar_space-3,this.Height - y_margin - y_in_margin);
     e.Graphics.DrawString(Convert.ToInt32(dataSource.Rows[i]
      [dataMember_Y].ToString()).ToString(),font, PrimaryValueBrush, point1);
     e.Graphics.DrawString(dataSource.Rows[i][dataMember_X].ToString()
      .Substring(0,(bar_width/font_size)+1),font, PrimaryValueBrush, point2);
    }
   }
   else
   {
    e.Graphics.DrawRectangle(AlternateColorPen,
     new Rectangle(x_margin+x_in_margin+(i+1)*bar_width+i*bar_space,
      this.Height - y_margin - y_in_margin - (int)(Convert.ToInt32
      (dataSource.Rows[i][dataMember_Y].ToString())* y_scale),
      bar_width,(int)
  (Convert.ToInt32(dataSource.Rows[i][dataMember_Y].ToString())* y_scale)));

    e.Graphics.FillRectangle(AlternatesolidBrush,x_margin+x_in_margin+(i+1)*
  bar_width+i*bar_space,this.Height - y_margin - y_in_margin -
  (int)(Convert.ToInt32(dataSource.Rows[i][dataMember_Y].ToString())* y_scale),
  bar_width,(int)
  (Convert.ToInt32(dataSource.Rows[i][dataMember_Y].ToString())* y_scale));

    using (Font font =
  new Font(this.Font.Name,font_size,this.Font.Style,this.Font.Unit))
    {
     Point point1 = new Point(x_margin+x_in_margin+(i+1)*bar_width+i*
  bar_space-3,this.Height - y_margin - y_in_margin -
  (int)(Convert.ToInt32
  (dataSource.Rows[i][dataMember_Y].ToString())* y_scale)-(int)font.Size-5);
     Point point2 = new Point(x_margin+x_in_margin+(i+1)*bar_width+i*
      bar_space-3,this.Height - y_margin - y_in_margin);
     e.Graphics.DrawString(Convert.ToInt32(dataSource.Rows[i]
      [dataMember_Y].ToString()).ToString(),font, AlternateValueBrush, point1);
     e.Graphics.DrawString(dataSource.Rows[i]
      [dataMember_X].ToString().Substring(0,(bar_width/font_size)+1)
      ,font, AlternateValueBrush, point2);
    }
   }
  }
  // Draw Baseline below bars
  e.Graphics.DrawLine(BaseLinePen,x_margin+x_in_margin,this.Height
       - y_margin - y_in_margin,this.Width-x_margin-x_in_margin,
      this.Height - y_margin - y_in_margin);

  // Draw Vertical Line
  e.Graphics.DrawLine(BaseLinePen,x_margin+x_in_margin,
      this.Height - y_margin - y_in_margin,x_margin+x_in_margin,
      y_margin + y_in_margin);
  //Dispose of Pens and Brushes.
  PrimaryColorPen.Dispose();
  AlternateColorPen.Dispose();
  BaseLinePen.Dispose();
  BorderLinePen.Dispose();
  PrimarysolidBrush.Dispose();
  AlternatesolidBrush.Dispose();
  PrimaryValueBrush.Dispose();
  AlternateValueBrush.Dispose();
 }
 catch(Exception ex)
 {
  string str = ex.Message;
 }
}
#endregion DrawBarChart method

Call DrawBarChart method in Paint event:

C#
#region Event Handlers
private void Barchart_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
 DrawBarChart(e);
}
#endregion Event Handlers

How To Use this Control in Test Project

Add Control to Form: Once the Charting project is compiled, add the control to the ToolBox -> My User Controls, using Add / Remove Items option.

Image 5

After the User control is added to the toolbox, the toolbox should look like this:

Image 6

Add a "Windows Application" project to the solution and name it "TestBarchart". Add a Windows Form "Form1" and place the Barchart control on the form from Toolbox.

Image 7

Setup the DB Server and Connections:

Image 8

Open the Server Explorer and Add Data connection as shown above. Add DataAdapter to the form by dragging the table on to the form and following the wizard. Generate Dataset and Preview data. Then the data objects should be seen like this:

Image 9

Set Properties for the control:

Image 10

You can change the AlternateFillColor, AlternateLineColor, AlternateValueColor, BaselineColor, BorderLineColor from defaults. Set the Datasource property to dataset11.ControlSource.

Set the DataMemberX to Xvalues and DataMemberY to Yvalues. And run the TestBarchart project.

Points of Interest

There are lots of features that can be added to the control, which I did not touch in this article as I wanted only the minimal features. Many events can be added to the control to allow the control to dynamically refresh whenever there is a change in the datasource.

Comments

Please take the time to vote for this article and/or to comment about it.

History

  • 08/22/2007 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Program Manager SCI
India India
Mattegunta is a Technical Consultant/Architect. He is Master of Technology from Jawaharlal Nehru Technological University, Hyderabad, India. Currently he is engaged in .net technologies like ASP.Net (VB.Net/C#), Smart Client, Web Services, SOA etc. He has been working with different Languages, Technologies and Platforms. VB6.0,ASP, COM/DCOM/COM+, Site Server, Exchange Server, J2EE, ASP.Net, Web Services, Smart Client, Windows, Linex etc during his career.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey14-Mar-12 22:09
professionalManoj Kumar Choubey14-Mar-12 22:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.