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

How to make a user control to show a simple mathematical graph in J#

, 14 Feb 2005
Rate this:
Please Sign up or sign in to vote.
This article will show you how to make a user control to 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 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:

Background color and image

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())}
            );
    }
}

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. 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

Share

About the Author

Lars-Inge Tønnessen
Web Developer
Norway Norway
No Biography provided

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 14 Feb 2005
Article Copyright 2005 by Lars-Inge Tønnessen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid