How to Make a User Control Show a Simple Mathematical Graph in J#





4.00/5 (2 votes)
Feb 14, 2005
3 min read

43713

239
This article will show you how to make a user control draw a mathematical graph on a panel using J#.
Introduction
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).
Get to the Point!
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 handler 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:
Background Color and Image
We want our graph control to look nice, so we will give 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 background 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())}
);
}
}
Titles
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. The 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 Graph Points
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 X and Y Lines
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
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);
}
}
Add the User Control to an Application
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 );
Source Files
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.
Conclusion
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#.
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.