Click here to Skip to main content
15,887,821 members
Articles / Desktop Programming / Windows Forms

A Simple Graph Control

Rate me:
Please Sign up or sign in to vote.
4.87/5 (15 votes)
5 Mar 2010CPOL3 min read 101.9K   11.7K   96   28
A simple control to draw graphs of points as a function of time
GraphControl_src

Introduction

Here's yet another graph control. I had to write it because I did not find any other easy to use Graph control which met my needs. This one may be the one you are looking for. I think the code is quite fast too.

This code is not too heavy, you can easily integrate it into your project. This code can also be used as a separate module that you can link to your own project.

Using the Code

First of all, add the using clause:

C#
using WindowsFormsControlLibrary;

Creating the Control

The first thing to do is to create an instance of the control.

There are three constructors:

  • The default constructor public GraphControl() using Arial fonts, as follows:
  • C#
    _CurveFont = new Font("Arial", 15);
    _CurveLegendFont = new Font("Arial", 10);
    _AxeFont = new Font("Arial", 8);
  • Another constructor allowing to specify your fonts:
  • C#
    public CurveControl(Font TitleFont, Font CurveLegendFont, Font AxeFont)
  • A third constructor allowing to specify your fonts and the format to use on the horizontal axis to display the Date and Time which defaults to "ddd MMM-d hh:mm" in the 2 first constructors and if the provided format is invalid:
  • C#
    public GraphControl(Font TitleFont, Font GraphLegendFont, 
    			Font AxeFont, string DTFormat)

The Graph control can then be created as follows:

C#
//Create a Graph Control
GraphCtrl = new GraphControl(new Font("Arial", 12), 
		new Font("Arial", 10), new Font("Arial", 8));
GraphCtrl.TabIndex = 0;
GraphCtrl.Dock = DockStyle.Fill;
GraphCtrl.Name = "Graph 1";

//Add Graph Control to the form or a panel on the form
GraphPanel.Controls.Add(GraphCtrl);

Adding Series

The Graph control contains three main properties:

  • A dictionary of a series of points
  • The primary axis
  • The secondary axis

It is needed to add (create) the series of points which will be empty at the beginning. To do so, call the AddPointsSerie method:

C#
//Add series
GraphCtrl.AddPointsSerie("Serie1", Axes.VerticalPrimary, "Serie 1", Color.Blue);
GraphCtrl.AddPointsSerie("Serie2", Axes.VerticalPrimary, "Serie 2", Color.Orange);
GraphCtrl.AddPointsSerie("Serie3", Axes.VerticalSecondary, "Serie 3", Color.DarkGreen);

The AddPointsSerie method takes four parameters:

  • A key used to identify the series
  • The vertical axis used to display the series
  • The displayed label of the series
  • The color of the series

The axis is taken from the two vertical axes available in the Axes enum.

Adding Points

It is now needed to populate the series with the points.

To retrieve the series, use the GetPointsSerie method, then call one of the three AddPoint methods:

C#
GraphCtrl.GetPointsSerie("Serie1").AddPointD(dt.AddHours(i), y1);

The three AddPoint methods are:

  • C#
    public void AddPointF(DateTime t, float Y)
  • Add a point whose Y value is a float.

  • C#
    public void AddPointD(DateTime t, double Y)
  • Add a point whose Y value is a double.

  • C#
    public void AddPointS(DateTime t, string Y)
  • Add a point whose Y value is a float provided as a string. If an invalid string is given, the point is ignored.

Updating the Display

If anything is changed like adding points to series, for example, you need to Invalidate the control:

C#
CC.Invalidate();

Changing the Axis of a Series

It may be needed to change the axis a series is associated with. To do so, call the SetSerieAxe method:

C#
CC.SetSerieAxe("Serie2", Axes.VerticalPrimary);

Clearing a Series

C#
CC.GetPointsSerie("Serie2").ClearPointsArray();

Removing a Series

C#
CC.RemovePointsSerie("Serie2");

Printing and Copying to the Clipboard

It is possible to print the Graph or copy the Graph to the clipboard using the new method:

C#
public void DrawGraph(Graphics g, Rectangle bounds)

The method takes 2 parameters:

  • A Graphics object
  • A Rectangle to draw the Graph onto

Using the Method to Print

C#
private void PrintDocument1_PrintPage
	(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
   GraphCtrl.DrawGraph(e.Graphics, e.MarginBounds);
}

Using the Method to Copy to the Clipboard

C#
private void CopyToolStripMenuItem_Click(object sender, EventArgs e)
{
   Bitmap Bmp = new Bitmap(800, 400); Bmp.SetResolution(100, 100);
   Graphics g = Graphics.FromImage(Bmp);
   g.Clear(Color.White);
   GraphCtrl.DrawGraph(g, new Rectangle(new Point(0, 0), Bmp.Size));
   Clipboard.SetImage(Bmp);
}

Interactivity

Graph Displayed Name

The user has the ability to double click on the Graph displayed name to change it.

Serie Selection

Clicking on or close to a serie selects it.

History

  • February 25, 2010 - Article first published
  • March 3, 2010
    • Added capability to the control to re-draw itself when resized
    • Created a separated Draw function to allow its usage for Painting, Printing, Copying...
    • Changed default format to display dates on the horizontal axis and added a 3rd constructor to specify it if needed
    • Added MajorUnitDateTime in the GraphAxe class, not yet used
    • Renamed everything using Graph instead of Curve

License

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


Written By
Other HP
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionImporting the key file Pin
Paul G. Scannell4-Mar-20 5:53
Paul G. Scannell4-Mar-20 5:53 
QuestionGood Work....... Pin
Nirav Chauhan30-Jul-13 3:28
Nirav Chauhan30-Jul-13 3:28 
GeneralMy vote of 5 Pin
neoraltech25-May-13 1:02
neoraltech25-May-13 1:02 
Questionwell done Pin
Member 93476012-Sep-12 23:59
Member 93476012-Sep-12 23:59 
AnswerRe: well done Pin
slelong3-Sep-12 6:50
slelong3-Sep-12 6:50 
GeneralMy vote of 5 Pin
Gmust14-Sep-10 7:36
Gmust14-Sep-10 7:36 
Questionhow does it compare with System.Windows.Forms.DataVisualization.Charting ? Pin
Louis T Klauder Jr2-Mar-10 5:28
professionalLouis T Klauder Jr2-Mar-10 5:28 
AnswerRe: how does it compare with System.Windows.Forms.DataVisualization.Charting ? Pin
slelong2-Mar-10 20:06
slelong2-Mar-10 20:06 
GeneralRe: how does it compare with System.Windows.Forms.DataVisualization.Charting ? Pin
Louis T Klauder Jr3-Mar-10 3:33
professionalLouis T Klauder Jr3-Mar-10 3:33 
GeneralRe: how does it compare with System.Windows.Forms.DataVisualization.Charting ? Pin
slelong3-Mar-10 9:05
slelong3-Mar-10 9:05 
GeneralRe: how does it compare with System.Windows.Forms.DataVisualization.Charting ? Pin
Louis T Klauder Jr3-Mar-10 15:55
professionalLouis T Klauder Jr3-Mar-10 15:55 
When using System.Windows.Forms.DataVisualization.Charting,
the following illustrates one way to get interactivity when
a user clicks on one of the plotted points:

In the designer for the window that contains the chart,
select the chart, and go to its Properties/Events panel, and
for event MouseClick, specify a function such as chart1_MouseClick
whose outline is as follows:

<br />
      private void chart1_MouseClick( object sender, MouseEventArgs e )<br />
      {<br />
         HitTestResult result = Chart1.HitTest( e.X, e.Y );<br />
<br />
         if ( result.ChartElementType == ChartElementType.DataPoint )<br />
         {<br />
            i_point_selected = result.PointIndex;<br />
            run_name_and_yyyy_mm_dd_nn = runs_on_spiral_List[ i_point_selected ];<br />
            if ( e.Button == MouseButtons.Left )<br />
            {<br />
               cl_run_info_Form run_info_Form = new cl_run_info_Form( );<br />
<br />
               //Initialize members<br />
<br />
               run_info_Form.ChartRef = Chart1;<br />
               run_info_Form.i_point_selected = i_point_selected;<br />
                .....<br />
               run_info_Form.db_context = db_context;<br />
<br />
               run_info_Form.ShowDialog( this );<br />
<br />
            }<br />
            else if ( e.Button == MouseButtons.Right )<br />
            {<br />
               cl_period_edit_Form period_edit_Form = new cl_period_edit_Form( );<br />
<br />
               period_edit_Form.ChartRef = Chart1;<br />
               period_edit_Form.i_point_selected = i_point_selected;<br />
               .....<br />
               period_edit_Form.db_context = db_context;<br />
<br />
               period_edit_Form.ShowDialog( this );<br />
            }<br />
<br />
         }<br />
         else if ( result.ChartElementType != ChartElementType.Nothing )<br />
         {<br />
            string elementType = result.ChartElementType.ToString( );<br />
            MessageBox.Show( this, "you did not right or left click on a data point" );<br />
         }<br />
      }<br />


Earlier I wrote a charting program including user interactivity
following the techniques spelled out in Petzold's wonderful book
"Programming Microsoft Windows with C#" published in 2002. That
lets you put on the screen exactly what you want, but it involves
vastly more code, so it was a great relief to be able to use
System.Windows.Forms.DataVisualization.Charting for the simpler
charting requirements of the more recent program.

For the interactive aspect of the earlier program the main
mechanism is panels for recognizing mouse actions within
the panels. Some snippets of code I needed to use are:

<br />
      // for adjusting arcs via mouse in main window<br />
<br />
      cl_Panel_collection  arc_boundary_select_panel_col;<br />
      public TrackBar    arc_bound_location_track_bar ;<br />
      int   index_of_arc_bound_selected ;<br />


<br />
         arc_boundary_select_panel_col = new cl_Panel_collection(this) ;<br />
         this.ContextMenu = this.contextMenu_arc_adjust ;         <br />
         change_arc_adjust_state_to( arc_adjust_state_enum.none ) ;<br />


<br />
   arc_boundary_select_panel_Point = new Point( x_panel_left_pixels, y_m1_pixels );<br />
   arc_boundary_select_panel_Size = new Size( panel_width_pixels, panel_height_pixels );<br />
   arc_boundary_select_panel_Rectangle = new Rectangle( arc_boundary_select_panel_Point, arc_boundary_select_panel_Size );<br />
<br />
   arc_boundary_select_panel_col.add_Panel( arc_boundary_select_panel_Rectangle );<br />
<br />
   arc_boundary_select_panel_col[ arc_boundary_select_panel_col.Count - 1 ].MouseHover<br />
         += new System.EventHandler( boundary_select_panel_MouseHover );<br />
<br />
   arc_boundary_select_panel_col[ arc_boundary_select_panel_col.Count - 1 ].MouseLeave<br />
         += new System.EventHandler( boundary_select_panel_MouseLeave );


<br />
         // <br />
         // contextMenu_arc_adjust<br />
         // <br />
         this.contextMenu_arc_adjust.MenuItems.AddRange( new System.Windows.Forms.MenuItem[ ] {<br />
            this.menuItem_move_arc_boundary,<br />
            this.menuItem_split_arc,<br />
            this.menuItem_remove_arc,<br />
            this.menuItem_confirm_boundary_move,<br />
            this.menuItem_cancel_boundary_move} );<br />
         this.contextMenu_arc_adjust.Popup += new System.EventHandler( this.contextMenu_arc_adjust_Popup );<br />
         // <br />
         // menuItem_move_arc_boundary<br />
         // <br />
         this.menuItem_move_arc_boundary.Index = 0;<br />
         this.menuItem_move_arc_boundary.Text = "move arc boundary";<br />
         this.menuItem_move_arc_boundary.Click += new System.EventHandler( this.menuItem_move_arc_boundary_Click );<br />
         // <br />
         // menuItem_split_arc<br />
         // <br />
         this.menuItem_split_arc.Index = 1;<br />
         this.menuItem_split_arc.Text = "split arc";<br />
         this.menuItem_split_arc.Click += new System.EventHandler( this.menuItem_split_arc_Click );<br />
         // <br />
         // menuItem_remove_arc<br />
         // <br />
         this.menuItem_remove_arc.Index = 2;<br />
         this.menuItem_remove_arc.Text = "remove arc";<br />
         this.menuItem_remove_arc.Click += new System.EventHandler( this.menuItem_remove_arc_Click );<br />


<br />
      private void menuItem_move_arc_boundary_Click(object sender, System.EventArgs e)<br />
      {<br />
         if ( arc_adjust_state != arc_adjust_state_enum.showing_arc_adj_function_context_menu )<br />
         {<br />
            throw(new Exception(String.Format<br />
               (  " {0} {1} "<br />
               ,  "function menuItem_confirm_boundary_move_Click was called with "<br />
               ,  "arc_adjust_state =", arc_adjust_state.ToString( )<br />
               ,  "indicating a logic error"<br />
               ))) ;<br />
         }<br />
<br />
         arc_bound_location_track_bar.Enabled = true ;<br />
         change_arc_adjust_state_to( arc_adjust_state_enum.responding_to_arc_bound_move_track_bar ) ;<br />
      }<br />


<br />
      private void menuItem_confirm_boundary_move_Click(object sender, System.EventArgs e)<br />
      {<br />
         double   new_position ;<br />
<br />
         new_position = arc_bound_location_track_bar.Value ;<br />
<br />
         move_arc_boundary_to( this.index_of_arc_bound_selected, new_position ) ;<br />
      <br />
         remove_track_bar( ) ;<br />
         change_arc_adjust_state_to( arc_adjust_state_enum.none ) ;<br />
      }<br />
Louis T Klauder Jr, Quarryville, PA, lklauder@wsof.com

GeneralRe: how does it compare with System.Windows.Forms.DataVisualization.Charting ? Pin
slelong5-Mar-10 6:49
slelong5-Mar-10 6:49 
GeneralEdit Name and selection Pin
slelong26-Feb-10 12:06
slelong26-Feb-10 12:06 
Generalrunable sample Pin
Mr.PoorEnglish26-Feb-10 2:35
Mr.PoorEnglish26-Feb-10 2:35 
GeneralRe: runable sample [modified] Pin
slelong26-Feb-10 6:31
slelong26-Feb-10 6:31 
GeneralRe: runable sample Pin
Mr.PoorEnglish26-Feb-10 9:40
Mr.PoorEnglish26-Feb-10 9:40 
Generalmatrix and graphicspath Pin
Mr.PoorEnglish26-Feb-10 10:28
Mr.PoorEnglish26-Feb-10 10:28 
GeneralRe: matrix and graphicspath Pin
slelong26-Feb-10 11:58
slelong26-Feb-10 11:58 
GeneralRe: runable sample Pin
slelong26-Feb-10 11:54
slelong26-Feb-10 11:54 
GeneralRe: runable sample Pin
Mr.PoorEnglish26-Feb-10 13:05
Mr.PoorEnglish26-Feb-10 13:05 
GeneralRe: runable sample Pin
slelong26-Feb-10 22:01
slelong26-Feb-10 22:01 
GeneralRe: runable sample Pin
Mr.PoorEnglish26-Feb-10 23:45
Mr.PoorEnglish26-Feb-10 23:45 
GeneralRe: runable sample Pin
slelong27-Feb-10 8:13
slelong27-Feb-10 8:13 
GeneralStrange article title man, its a graph Pin
Sacha Barber25-Feb-10 20:11
Sacha Barber25-Feb-10 20:11 
GeneralRe: Strange article title man, its a graph Pin
Xmen Real 26-Feb-10 4:08
professional Xmen Real 26-Feb-10 4:08 

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.