![]() |
Desktop Development »
Miscellaneous »
Miscellaneous Controls
Intermediate
Building a Graph control in 7 easy steps.By Harshawardhan DesaiThis article shows you how to build a simple user control by creating a graph control in 7 easy steps. |
C#.NET 1.0, Win2K, WinXP, Visual Studio, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

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:
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.
Before we delve further into details of the GraphControl, let me define other structures and enumerations defined in the UserControls namespace
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:

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.
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)
GraphType public enum GraphType
{
Line,
Bar
}
The above enumeration is used to decide the style of the graph.BarGraph or LineGraph.
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:
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:
xDiff between origin and last co-ordinate of X-axis
xStep which is Points/Co-ordinates ratio. This will be the distance between every co-ordinate drawn on the X-axis.
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:
So to find the exact location of (1999,66) on Y-axis we do the following:
diffY. i.e. 150 -100 = 50.
diffValue i.e. 70 - 60 = 10.
unitsPerCoordinate=diffValue/diffY i.e. 10/50 = 0.2.
Points value. i.e. 66-60 = 6.
graphPoints ahead of 60.
finalYValue of 66 will be equal to location of 60 + (6 / 0.2) i.e. 100 + 30 = 130.
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 ..
Point from the the Points list.
FindLocationOnGraph
FloatPoint prev
FloatPoint current.
prev to current
ShowPointsOnGraph is true then paint the co-ordinates.
current point into prev
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:
graphRegion, where the graph will be drawn.
GraphStyle property, we draw the graph.
There you have it! 7 easy steps to build the GraphControl!!
I wrote this code using SharpDevelop. You can just compile the DemoForm.cs at command prompt if you don't use an IDE.
Points by adding points using the text boxes and the button Add
AddPoints function.
Main and uncomment the second line to see another graph demo.
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.
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 12 Mar 2003 Editor: Smitha Vijayan |
Copyright 2003 by Harshawardhan Desai Everything else Copyright © CodeProject, 1999-2009 Web10 | Advertise on the Code Project |