![]() |
Languages »
Other .NET Languages »
J# .NET
Intermediate
How to make a user control to show a simple mathematical graph in J#By Lars-Inge TønnessenThis article will show you how to make a user control to draw a mathematical graph on a panel using J#. |
Windows, .NETVS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

Let�s say you have made a few cool mathematical J# applications and want to show the results graphically. The Visual Studio IDE does not come with a graph user control, so what to do next? You could buy a user control from a third party, or make one for yourself. This graph control will only work for positive numbers. It�s up to you to make this work for negative numbers. This is not an article for showing how to build an advanced mathematical graph control, but an article that will show you how to build a custom user control using custom painting in J#. We will be giving the graph, x and y points in an array, and not a mathematical function f(x).
Let�s stop muttering and get busy writing the coding. Open Visual Studio and create a new J# Windows Control Library project.

When this article was written in J# (.NET 1.0 Visual Studio 2002 and 1.1
Visual Studio 2003) it did not support operator overloading. In
C#/VB.NET/Managed C++ we could have overridden the OnPaint method
to be able to paint on the Panel. In J# we register a paint handler
with the Panel. To register a paint hander with the
Panel please do this in the constructor:
// This will register the paint event handler.
this.add_Paint( new System.Windows.Forms.PaintEventHandler(this.Paint) );
This is how the Paint method will look like:
/**
* This is our paint handler that can draw onto the panel.
*/
private void Paint( System.Object object,
System.Windows.Forms.PaintEventArgs args )
{
System.Drawing.Graphics g = args.get_Graphics();
}
The System.Drawing.Graphics provides us with basic drawing
functions to the Panel. Among them are functions to draw lines,
pies, images, strings and so on. You can read more about the
Graphics class in the MSDN library:
We want our graph control to look nice, so we will let the application writer, the capability to change the background color. As a bonus we will give the control a nice image background property. With a background image we can give a nice 3D-effect:
private System.Drawing.Color _backgroundColor = System.Drawing.Color.get_White();
private System.Drawing.Image _backgroundImage = null;
/** @property */
public System.Drawing.Color get_BackgroundColor( )
{
return this._backgroundColor;
}
/** @property */
public void set_BackgroundColor( System.Drawing.Color _in )
{
this._backgroundColor = _in;
}
/** @property */
public void set_BackgroundImage( System.Drawing.Image _img )
{
this._backgroundImage = _img;
}
/** @property */
public System.Drawing.Image get_BackgroundImage( )
{
return this._backgroundImage;
}
/**
* Sets the background image or color.
*/
private void graph_SetBGColorImage( System.Drawing.Graphics g )
{
// Sets the backbround color.
this.set_BackColor( this._backgroundColor );
// Sets a background image
if ( this._backgroundImage != null )
{
System.Drawing.TextureBrush textureBrush =
new System.Drawing.TextureBrush(
this._backgroundImage,
System.Drawing.Drawing2D.WrapMode.TileFlipXY );
g.FillRectangles(
textureBrush,
new System.Drawing.Rectangle[]{new System.Drawing.Rectangle(0,
0, this.get_Width(), this.get_Height())}
);
}
}
Our graph control must have a heading or a title . To do so we will be using a title, title color and a font property for the application:
private System.Drawing.Font _titleFont = this.get_Font();
private String _title = "";
private System.Drawing.Color _titleColor =
System.Drawing.Color.get_Black();
/** @property */
public void set_Title( String _in )
{
this._title = new String( _in );
}
/** @property */
public String get_Title( )
{
return new String( this._title );
}
public void set_TitleFont( System.Drawing.Font _font )
{
this._titleFont = (System.Drawing.Font)_font.Clone();
}
/**
* Gets the title font
*/
/** @property */
public System.Drawing.Font get_TitleFont( )
{
return (System.Drawing.Font)this._titleFont.Clone();
}
/** @property */
public System.Drawing.Color get_TitleColor( )
{
return this._titleColor;
}
/** @property */
public void set_TitleColor( System.Drawing.Color _color )
{
this._titleColor = _color;
}
The title should be centered on the Panel, so we will calculate
the center of the window and the center of the title text width. Center of the
title minus the center of the window width will give us the start X position of
the title. Please remember to take action if the title is too wide for the
panel. Next step is to calculate or give a Y position. We put this code in a
separate private method. We measure the title string with
the following code:
float titleWidth = g.MeasureString( this._title,
this._titleFont).get_Width();
The method for showing the title will be:
private void graph_ShowTitle( System.Drawing.Graphics g )
{
try
{
// The panel width
int panelWidth = this.get_Width();
// The title width
float titleWidth = g.MeasureString( this._title,
this._titleFont).get_Width();
// Calculate the title start X position.
int theTitleXPos =
System.Convert.ToInt32((panelWidth/2.0f)-(titleWidth/2.0f));
// Calculate the font height Y position.
int theTitleYPos = int theTitleYPos = 10;
// If the title is to lagre for the panel width.
if ( theTitleXPos < 0 ) theTitleXPos = 0;
// Draw the title string
g.DrawString( this._title,
this._titleFont,
new System.Drawing.SolidBrush(this._titleColor),
theTitleXPos,
theTitleYPos,
System.Drawing.StringFormat.get_GenericDefault()
);
}
catch ( System.Exception ee )
{
System.Windows.Forms.MessageBox.Show( ee.get_Source() );
}
}
The points on the graph are given as an array of PointF to a
property:
private System.Drawing.PointF[] _points = null;
/** @property */
public System.Drawing.PointF[] get_Points( )
{
return this._points;
}
The application writer must be able to give the X and Y lines colors. We
insert a new Color property into our control:
private System.Drawing.Color _XYLineColor =
System.Drawing.Color.get_White();
/** @property */
public System.Drawing.Color get_XYLineColor( )
{
return this._XYLineColor;
}
/** @property */
public void set_XYLineColor( System.Drawing.Color _col )
{
this._XYLineColor = _col;
}
The graph drawing method will look like this:
/**
* Draws the X and Y lines
*/
private void graph_DrawXYLines( System.Drawing.Graphics g )
{
if ( this._points.length > 1 )
{
// Used to hold the smallest X position.
float _minX = this._points[0].get_X();
// Used to hold the largest X position.
float _maxX = this._points[0].get_X();
// Used to hold the largest Y value
float _maxY = this._points[0].get_Y();
// Used to hold the smallest Y value
float _minY = this._points[0].get_Y();
// Go through all points and find the smallest
// and largest values.
for (int counter = 0; counter < this._points.length; counter++)
{
if ( _maxX < this._points[counter].get_X() ) _maxX =
this._points[counter].get_X();
if ( _minX > this._points[counter].get_X() ) _minX =
this._points[counter].get_X();
if ( _minY > this._points[counter].get_Y() ) _minY =
this._points[counter].get_Y();
if ( _maxY < this._points[counter].get_Y() ) _maxY =
this._points[counter].get_Y();
}
// Calculates X scala
float _rateX = 0.0f;
int startX = 0;
if ( (_maxX > 0 ) && (( _minX > 0 ) || (_minX == 0)) )
{
_rateX = ( _maxX ) / ( this.get_Width() );
startX =
System.Convert.ToInt32(((_maxX )/_rateX)-this.get_Width());
}
else
{
System.Windows.Forms.MessageBox.Show("Sorry, not supported.");
Application.Exit();
}
// Calculates the Y scala
float _rateY = 0.0f;
int startY = 0;
if ( ( _maxY > 0 ) & (( _minY > 0 ) || (_minY == 0)) )
{
_rateY = ( _maxY ) / ( this.get_Height() );
startY = System.Convert.ToInt32(( _maxY ) / _rateY);
}
else
{
System.Windows.Forms.MessageBox.Show("Sorry, not supported.");
Application.Exit();
}
// Y-line
g.DrawLine( new System.Drawing.Pen( new
System.Drawing.SolidBrush(this._titleColor), 2.0f),
System.Convert.ToInt32( startX ),
System.Convert.ToInt32( 0 ),
System.Convert.ToInt32( startX ),
System.Convert.ToInt32( this.get_Height() )
);
// X-line
g.DrawLine( new System.Drawing.Pen( new
System.Drawing.SolidBrush(this._titleColor), 2.0f),
System.Convert.ToInt32( 0 ),
System.Convert.ToInt32( startY ),
System.Convert.ToInt32( this.get_Width() ),
System.Convert.ToInt32( startY )
);
// Draw the graph line
for (int countThroughAllPoints = 0;
countThroughAllPoints < this._points.length; countThroughAllPoints++)
{
System.Drawing.PointF point = this._points[countThroughAllPoints];
if ( countThroughAllPoints > 0 )
{
System.Drawing.PointF prevPPoint =
this._points[countThroughAllPoints - 1];
int prevXpoint =
System.Convert.ToInt32( prevPPoint.get_X() / _rateX );
int prevYPoint = System.Convert.ToInt32(
this.get_Height()-(prevPPoint.get_Y()/_rateY));
int thisXpoint =
System.Convert.ToInt32( point.get_X() / _rateX );
int thisYpoint =
System.Convert.ToInt32(this.get_Height()-(point.get_Y()/_rateY));
// Draws the x marks
g.DrawLine( new System.Drawing.Pen( new
System.Drawing.SolidBrush(this._titleColor), 2.0f),
System.Convert.ToInt32( thisXpoint ),
System.Convert.ToInt32( startY-5 ),
System.Convert.ToInt32( thisXpoint ),
System.Convert.ToInt32( startY+5 )
);
// Draws the y marks
g.DrawLine( new System.Drawing.Pen( new
System.Drawing.SolidBrush(this._titleColor), 2.0f),
System.Convert.ToInt32( startX-5 ),
System.Convert.ToInt32( thisYpoint ),
System.Convert.ToInt32( startX+5 ),
System.Convert.ToInt32( thisYpoint )
);
// Draws the point x string
g.DrawString( ""+point.get_X(),
this._titleFont,
new System.Drawing.SolidBrush( this._XYLineColor ),
System.Convert.ToInt32(thisXpoint - System.Math.Ceiling(
g.MeasureString( ""+point.get_X(),
this._titleFont).get_Width())),
startY - this._titleFont.get_Height() );
// Draws the point y string
g.DrawString( ""+point.get_Y(),
this._titleFont,
new System.Drawing.SolidBrush( this._XYLineColor ),
startX,
thisYpoint);
// Draws the graph line
g.DrawLine(new System.Drawing.Pen(new
System.Drawing.SolidBrush(this._XYLineColor),2.0f),
prevXpoint,
prevYPoint,
thisXpoint,
thisYpoint
);
}
else
{
int thisXpoint =
System.Convert.ToInt32( point.get_X() / _rateX );
int thisYpoint =
System.Convert.ToInt32(this.get_Height()-point.get_Y()/_rateY);
// Draws the x marks
g.DrawLine( new System.Drawing.Pen( new
System.Drawing.SolidBrush(this._titleColor), 2.0f),
System.Convert.ToInt32( thisXpoint ),
System.Convert.ToInt32( startY-5 ),
System.Convert.ToInt32( thisXpoint ),
System.Convert.ToInt32( startY+5 )
);
// Draws the y marks
g.DrawLine( new System.Drawing.Pen( new
System.Drawing.SolidBrush(this._titleColor), 2.0f),
System.Convert.ToInt32( startX-5 ),
System.Convert.ToInt32( thisYpoint ),
System.Convert.ToInt32( startX+5 ),
System.Convert.ToInt32( thisYpoint )
);
// Draws the x line string
g.DrawString( ""+point.get_X(),
this._titleFont,
new System.Drawing.SolidBrush( this._XYLineColor ),
System.Convert.ToInt32(thisXpoint - System.Math.Ceiling(
g.MeasureString( ""+point.get_X(),
this._titleFont).get_Width())),
startY - this._titleFont.get_Height());
// Draws the y line string
g.DrawString( ""+point.get_Y(),
this._titleFont,
new System.Drawing.SolidBrush( this._XYLineColor ),
startX,
thisYpoint);
}
}
Put a Panel on your application, resize the Panel
to the size of your user control. Add the user control to the Panel
with this code. Please remember to add a reference to the user control DLL
file:
GraphUserControl.DrawGraph GraphCtl =
new GraphUserControl.DrawGraph();
GraphCtl.set_Dock( System.Windows.Forms.DockStyle.Fill );
// Set a few properties.
GraphCtl.set_Title( "This is a title" );
GraphCtl.set_TitleFont(new
System.Drawing.Font(System.Drawing.FontFamily.get_GenericSansSerif(),
12.0f));
GraphCtl.set_TitleColor( System.Drawing.Color.get_Blue() );
GraphCtl.set_BackgroundColor(System.Drawing.Color.get_White() );
GraphCtl.set_BackgroundImage(System.Drawing.Image.FromFile(
"..\\..\\background.bmp"));
GraphCtl.set_XYLineColor( System.Drawing.Color.get_BlueViolet() );
// The point to draw
System.Drawing.PointF[] pointsF = new System.Drawing.PointF[]
{
new System.Drawing.PointF(1.2f,2.3f),
new System.Drawing.PointF(2.4f,7.5f),
new System.Drawing.PointF(5.1f,5.3f)
};
// Transfer the points to the user control.
GraphCtl.set_Points( pointsF );
// Add the user control to the panel.
this.panel1.get_Controls().Add( GraphCtl );
The Microsoft Visual Studio 2003 solution is stored in the \GraphUserControl\GraphUserControl file. This will open both the user control and the test application projects.
As we have seen in this article it is not possible to override the
Paint method in J#. The tricky part is that we have to implement a
paint handler in J#.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 14 Feb 2005 Editor: Rinish Biju |
Copyright 2005 by Lars-Inge Tønnessen Everything else Copyright © CodeProject, 1999-2009 Web20 | Advertise on the Code Project |