Click here to Skip to main content
Click here to Skip to main content

Building a Graph control in 7 easy steps.

, 12 Mar 2003
Rate this:
Please Sign up or sign in to vote.
This article shows you how to build a simple user control by creating a graph control in 7 easy steps.

Sample Image - maximum width is 600 pixels

Introduction

C# provides rich features for building your own reusable controls. As we all know, third party controls are often expensive and don't have exactly the functionality we need, so we have to write extra code just to achieve some special functionality we might use over and over. Also we cannot tweak around the code of a third party component to change its behavior since we don't have the code.

So here's a graph control that you might need to add to your application, which you can simply add to your forms, and in a matter of minutes you have a graph displayed on your form. Its as easy as adding a Button or TextBox to your form.

The graph control provides the following features:

  1. Graph style: Line graph or bar graph
  2. Graph colors: You can set colors for display text, axes and actual graph.
  3. Displaying points: You can choose to show the textual representation of a coordinate along with the graph.
  4. Scales: Have different scales for X axis and Y axis and choose their titles.

Using the code

As you can see the the picture above, I have a demo form with the graph control and I have added other controls to this form to add/modify the features of the graph control at run time.

Let's walk through the building of GraphControl to understand how a user control is built.

Lets get started....

The GraphControl is defined like this :

namespace UserControls 
{
    public class GraphControl : System.Windows.Forms.UserControl
    {
     .......

We create a namespace UserControls and create a class GraphControl which is derived from UserControl, which defines it as a user control.

Structures and enumerations

Before we delve further into details of the GraphControl, let me define other structures and enumerations defined in the UserControls namespace

  1. GraphStruct
    public struct GraphStruct
    {
        public int PointsOnXAxis,PointsOnYAxis;
        public float StartValueXAxis,StartValueYAxis;
        public int StepXAxis,StepYAxis;
        ...
        ..
    }

    The above structure is used to initialize the graph and will determine how the axes are drawn and how the co-ordinates are laid out.

    PointsOnXAxis and PointsOnYAxis

    These variables will define, the number of points to lay out on the axes. For e.g. if x-axis is representing number of years in an engineering course and y-axis represents the percentage/grade of the student in every year, then PointsOnXAxis will be 4 and PointsOnYAxis will be 10.

    StartValueXAxis and StartValueYAxis

    These variables define what the first value on X-Axis and Y-Axis should be. Using the example above, the x-axis will start with 1994 if the course started in 1994 and y-axis starts with 10 since grades will range from 10% to 100%.

    StepXAxis and StepYAxis

    These variables will define the increments for co-ordinates on each axis. Again, using the example above, the steps on x-axis will be 1 since we increment from year to year, whereas increments on y-axis will be 10 since we increment in steps of 10.

    The graph for this example looks like:

    Grades

  2. FloatPoint
    public struct FloatPoint
    {
        public float X,Y,Value;
        ..
        .
    }

    This structure is used to represent a point on our graph. The reason why the class Point is inadequate for storing our points is because our co-ordinates are in float whereas Point takes integers only. Moreover, we also have an extra member called Value which is used to represent the actual display value.

    For e.g. we want to display a point (1994,75) on the graph.

    1. This point is split into two specific points on our graph (1994,0) on x-axis and (0,75) on y-axis.
    2. Now, 1994 or 57 are just display values. Their actual co-ordinates are calculated by a function in our class called FinLocationOnGraph which acts like the Windows' ScreenToClient function. So these points become float points with values like:

      X = 134 ,Y = 561, Value = 1994 = > represents (1994,0)

      X = 97, Y = 351 , Value = 57 = > represents (0,57)

  3. GraphType
    public enum GraphType
    {
        Line,
        Bar
    }

    The above enumeration is used to decide the style of the graph.BarGraph or LineGraph.

Functions

Now lets see how GraphControl is implemented ....

  • DrawAxes function
    //draws the X and Y axes and also set the 
    //origin and x and y axes points
    //This method should be called before any other drawing method.
    void DrawAxes(Graphics g)
    {
        //get the bounds of our region.We will draw within the region
        RectangleF rect = Region.GetBounds(g);
        float xOrigin =  rect.Left  + 20;
        float yOrigin =  rect.Bottom - 70;
                
        origin = new FloatPoint(xOrigin,yOrigin);
        xAxis = new FloatPoint(rect.Right - 20,origin.Y);
        yAxis = new FloatPoint(origin.X ,rect.Top);
                
        Pen axisPen = new Pen(axesColor);
                    
        g.DrawLine(axisPen,origin.X,origin.Y,xAxis.X,xAxis.Y);
        g.DrawLine(axisPen,origin.X,origin.Y,yAxis.X,yAxis.Y);
                            
        axisPen.Dispose();
    }

    This method draws the axes. It does the following things:

    1. Gets the bounds of the drawing area of the control.
    2. Defines the point for origin which will be at (left, bottom) point of the control
    3. Defines x-axis which will run from (left, bottom) to (right, bottom)
    4. Defines y-axis which will run from (left, bottom) to (left, top)
  • DrawPoints function
    //draws the co-ordinates on x and y axes.
    void DrawPoints(Graphics g)
    {
        float xDiff = xAxis.X - origin.X;
        float yDiff = origin.Y - yAxis.Y;
        float xStep = xDiff/GRAPHSTRUCT.PointsOnXAxis;
        float yStep = yDiff/GRAPHSTRUCT.PointsOnYAxis;
                
        FloatPoint fpt = new FloatPoint(origin.X,origin.Y);
        fpt.Value = 0;
        graphPointsX.Add(fpt);
        graphPointsY.Add(fpt);
                
        Pen   p = new Pen(textColor);
        Brush b = new SolidBrush(textColor);
        Font  f = new Font(Font.FontFamily,Font.Size);
        for(int i = 1; i<= GRAPHSTRUCT.PointsOnXAxis; i++)
        {
            float xAxisX = origin.X + (i * xStep);
            float xAxisY = origin.Y;
                    
            g.DrawLine(p,xAxisX,xAxisY - 2,xAxisX,xAxisY + 2);
            float val = GRAPHSTRUCT.StartValueXAxis + 
                            ((i-1) * GRAPHSTRUCT.StepXAxis) ;    
            g.DrawString(val.ToString(),f,b,xAxisX-5,xAxisY + 3);
                    
            fpt.X = xAxisX;
            fpt.Y = 0;
            fpt.Value = val;
            graphPointsX.Add(fpt);
        }
        for(int j = 1; j<= GRAPHSTRUCT.PointsOnYAxis; j++)
        {
            float yAxisX = origin.X;
            float yAxisY = origin.Y - (j * yStep);
                    
            g.DrawLine(p,yAxisX -2 ,yAxisY,yAxisX + 2,yAxisY);
            float val = GRAPHSTRUCT.StartValueYAxis + 
                          ((j-1) * GRAPHSTRUCT.StepYAxis) ;    
            g.DrawString(val.ToString(),f,b,yAxisX-15,yAxisY);
                    
            fpt.X = 0;
            fpt.Y = yAxisY;
            fpt.Value = val;
            graphPointsY.Add(fpt);
        }
        f.Dispose();
        b.Dispose();
        p.Dispose();
    }

    This function draws the co-ordinates on the X and Y axes.

    For X-axis:

    1. Calculates the diff xDiff between origin and last co-ordinate of X-axis
    2. Calculates xStep which is Points/Co-ordinates ratio. This will be the distance between every co-ordinate drawn on the X-axis.
    3. A small line and value of the co-ordinate will be drawn at each co-ordinate on X-axis.
    4. All these points will be saved in a list graphPointsX

    A similar procedure will be followed for Y-axis and the points will be stored in graphPointsY.

  • FindLocationOnGraph function.
    //Given a display point, 
    //this function will point the actual co-ordinates
    //for this point on our graph. 
    //It acts like the ScreenToClient function in Windows
    FloatPoint FindLocationOnGraph(Point pt)
    {
         float diffX,diffY,diffValue,finalXValue,finalYValue;
         diffX = diffY = -1;
         finalXValue = finalYValue = 0;
         for(int i=0;i < graphPointsX.Count;i++)
         {
             //store the current point
             FloatPoint current = (FloatPoint)graphPointsX[i];
             //if points X is lesser that current points Value
             if((float)pt.X < current.Value)
             {
                 FloatPoint previous = (FloatPoint)graphPointsX[i-1];
                 //store diff between current
                 // X and previous X coordinate
                 diffX = current.X - previous.X;
                 //store difference between values 
                 //of current and prev points
                 diffValue = current.Value - previous.Value;
                 float unitsPerCoordinate = diffValue/diffX;
                 finalXValue = 
                   ((pt.X - previous.Value)/unitsPerCoordinate) 
                   + previous.X;
                 break;
             }
             else if((float)pt.X == current.Value)
             {
                 finalXValue = current.X;
             }
         }
         for(int j=0;j < graphPointsY.Count;j++)
         {
             FloatPoint current = (FloatPoint)graphPointsY[j];
             if((float)pt.Y < current.Value)
             {
                 FloatPoint previous = (FloatPoint)graphPointsY[j-1];
                 diffY     = current.Y     - previous.Y;
                 diffValue = current.Value - previous.Value;
                 float unitsPerCoordinate = diffValue/diffY;
                 finalYValue = 
                      ((pt.Y - previous.Value)/unitsPerCoordinate) 
                      + previous.Y;
                 break;
             }
             else if((float)pt.Y == current.Value)
             {
                 finalYValue = current.Y;
             }
         }
         FloatPoint fpNew = new FloatPoint(finalXValue,finalYValue);
         return fpNew;
    }
    

    This function will return the actual co-ordinates of a point like (1996,66) by converting it into floating point co-ordinates that the graph control can understand.

    The code may seem difficult to understand but its really simple. Here is how it works..

    Lets assume the following things:

    1. The origin (0,0) is located at location (45,45) on the graph.
    2. The point (0,60) is located at (45,100) on Y-axis.
    3. The point (0,70) is located at (45,150) on Y-axis.

    So to find the exact location of (1999,66) on Y-axis we do the following:

    1. Find a point on Y axis greater than 66 .i.e. 70.
    2. Find the previous point which was lesser than 66. i.e. 60.
    3. Calculate difference between co-ordinates of these points diffY. i.e. 150 -100 = 50.
    4. Calculate difference between the two values diffValue i.e. 70 - 60 = 10.
    5. Calculate unitsPerCoordinate=diffValue/diffY i.e. 10/50 = 0.2.
    6. Calculate difference between the lesser value and our Points value. i.e. 66-60 = 6.
    7. This means the our point 66 is (6 / 0.2) graphPoints ahead of 60.
    8. So finalYValue of 66 will be equal to location of 60 + (6 / 0.2) i.e. 100 + 30 = 130.
    9. The point (0,66) will be positioned on Y-axis as (45,130).
    10. Similarly we find the X-axis position (1996, 0).
    11. We then create a new FloatPoint with finalXValue and finalYValue
  • DrawLineGraph function
    //draws the actual graph, Line style.
    void DrawLineGraph(Graphics g)
    {
        Pen p = new Pen(graphColor);
        Point start = (Point)Points[0];
        FloatPoint prev,current;
        prev = FindLocationOnGraph(start);
        for(int i=1 ; i < Points.Count ;i++)
        {
            Point pt = (Point)Points[i];
            current = FindLocationOnGraph(pt);    
            g.DrawLine(p,prev.X,prev.Y,current.X,current.Y);
            if(bShowPoints)
            {
                Brush b = new SolidBrush(textColor);
                Font f = new Font(Font.FontFamily,Font.Size);    
                string title = "(" + pt.X + "," + pt.Y + ")";
                if(prev.Y > current.Y)
                    g.DrawString(title,f,b,current.X - 25, 
                       current.Y - 15);
                else
                    g.DrawString(title,f,b,
                       current.X - 25, current.Y + 5);
                if(i ==1)
                {
                    title = "(" + start.X + "," + start.Y + ")";
                    g.DrawString(title,f,b,prev.X - 10, prev.Y - 15);
                }
                f.Dispose();
                b.Dispose();
            }
            prev = current;
        }
        p.Dispose();
    }

    This function draws the line graph. Its pretty simple. This is what it does ..

    1. Get the first Point from the the Points list.
    2. Find its location by passing it to FindLocationOnGraph
    3. Store this in FloatPoint prev
    4. Get the next point and repeat step 2 for it and store it in FloatPoint current.
    5. Draw a line from prev to current
    6. If the property ShowPointsOnGraph is true then paint the co-ordinates.
    7. If the line being drawn is sloping downwards then draw the co-ordinates above the line else below it.
    8. Store the current point into prev
    9. Repeat step 4 to 7 till you have drawn every point in the list.
  • OnPaint function
    //overridden to paint our graph
    protected override void OnPaint(PaintEventArgs pe)
    {
        Rectangle bound = new Rectangle(new Point(0,0),Size);
        graphRegion = new Region(bound);
        Region = graphRegion;
        DrawAxes(pe.Graphics);
        DrawPoints(pe.Graphics);
        DrawTitles(pe.Graphics);
        if(graphType == GraphType.Bar)
           DrawBarGraph(pe.Graphics);
        else
           DrawLineGraph(pe.Graphics);
        //drawAxes is called twice becuase 
        //the axes get overwwiten sometimes by other lines
        DrawAxes(pe.Graphics);
    }

    This is the most important function if you are building your own user control. Override this function to paint your control. This is where the real action happens:

    • Step 1. We first set the bounds of our drawing area by the size of the control.
    • Step 2. Then we use the bounds to define the graphRegion, where the graph will be drawn.
    • Step 3. Then we draw the axes.
    • Step 4. Then draw the points on the axes.
    • Step 5. Then draw the titles for the axes.
    • Step 6. And depending on the GraphStyle property, we draw the graph.
    • Step 7. We redraw the axes because some points may lie on the axis and may draw over it.

There you have it! 7 easy steps to build the GraphControl!!

Running the code

I wrote this code using SharpDevelop. You can just compile the DemoForm.cs at command prompt if you don't use an IDE.

  • Just compile and run the demo project and you can change the colors by choosing a radio button and then choosing a sample color from the combo-box.
  • You can add new Points by adding points using the text boxes and the button Add
  • The sample program shows different overloaded methods for AddPoints function.
  • Comment the 1st line in Main and uncomment the second line to see another graph demo.
  • If you are writing your code and want to manipulate the behavior of GraphControl at run time, do not forget to call Invalidate function of the control. This function forces the control to redraw itself.

Finally, I have provided the demowithclass.bat and demowithdll.bat files along with the code. The first one uses the graphcontrol.cs file along with the demo form. And the second one builds a graph.dll and the demo form loads the control from the DLL.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Harshawardhan Desai
Web Developer
United States United States
I have an experience of 5 years in software and it was mostly VC++ COM/DCOM. I recently took up studying C# and found it very easy and interesting.I got my MCAD in C# in Feb 2003 and used the articles on this site to leanr new things and expand my C# knowledge. I love this site and I want to post articles to help others learn C# easily and quickly.
When I am not programming, I listen to lots of music, watch lots of movies, read lots of non-computer books and am a self-improvement junkie.
I believe in doing whatever it takes to achive one's goals and you would rather be busy DOING things to make you succussful than sitting on the couch cribbing about how life is unfair!

Comments and Discussions

 
Questionmasla PinmemberMember 803962823-Jul-12 21:16 
GeneralMy vote of 3 PinmemberJack_3212-Mar-12 20:21 
QuestionGraph control using Visual C#.net ???? Pinmembergbfgdhrtfhyutyi11-Oct-06 19:49 
Questionneed assistance Pinmembergbfgdhrtfhyutyi11-Oct-06 19:07 
AnswerRe: need assistance Pinmemberpita200011-Oct-06 19:25 
Questionneed assistance Pinmembergbfgdhrtfhyutyi11-Oct-06 19:01 
Generalif x and y points > 1000 Pinmemberj23knight15-Jul-06 4:46 
QuestionError in adding graph.dll Pinmemberpradeepnatekar15-May-06 18:49 
AnswerRe: Error in adding graph.dll PinmemberoguzhanFilizlibay()29-Jun-08 3:33 
GeneralOptimizing for large data Pinmemberameya gholap7-Nov-05 20:01 
GeneralDrawLineGraph Pinmemberslyth15-Dec-04 3:23 
GeneralHelp setting up project Pinmemberalman24-May-03 3:35 
GeneralScrolling PinmemberSenthu18-Mar-03 3:49 
GeneralRe: Scrolling PinmemberHarshawardhan Desai18-Mar-03 9:34 
GeneralLooks good PinmemberMikeBeard14-Mar-03 5:32 
GeneralRe: Looks good PinmemberHarshawardhan Desai14-Mar-03 6:57 
GeneralRe: Looks good Pinmemberleppie14-Mar-03 10:19 
GeneralRe: Looks good PinmemberHarshawardhan Desai14-Mar-03 11:26 
GeneralRe: Looks good Pinmemberleppie14-Mar-03 12:23 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140821.2 | Last Updated 13 Mar 2003
Article Copyright 2003 by Harshawardhan Desai
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid