Click here to Skip to main content
15,881,757 members
Articles / Web Development / HTML

A simple C# library for graph plotting

Rate me:
Please Sign up or sign in to vote.
4.90/5 (252 votes)
4 Sep 2014CPOL2 min read 952.1K   86.7K   578   165
An easy to use C# library for quick and simple graph plotting.

Introduction

In our application, we had to display the output of a multichannel ECG (Electro Cardiograph) device. I had a look at some commercial libraries, but none of them met by demands. So, I decided to design a simple solution by myself.

This is my attempt to design a flexible, easy to use library for drawing graphs.

The library is capable of displaying multiple graphs in different layouts. Right now, five modes of display are possible:

  • Normal: means that all data sources are displayed in one graph window, with separate ordinates.
  • Stacked: means that all data sources are displayed in one graph window, stacked vertically, with shared ordinate and shared abscissa.
  • Vertical aligned: means that the graph windows are displayed vertically aligned, with separate ordinates and shared abscissa.
  • Tiled horizontal: means that the data sources are displayed in tiled windows (preferred alignment direction is horizontal).
  • Tiled vertically: means that the data sources are displayed in tiled windows (preferred alignment direction is vertical).

Graphs can be displayed unscaled or auto-scaled. In the auto-scale mode, the visible graph is automatically fit to the visible area.

The following images show examples for the different display modes:

Normal:

GraphPlotting/graph_normal.png

Stacked:

GraphPlotting/graph_stacked.png

Tiled horizontal:

GraphPlotting/graph_tiled_horizontal.png

Tiled vertical:

GraphPlotting/graph_tiled_vertically.png

Vertical aligned:

GraphPlotting/graph_vertically_aligned.png

Autoscaled X-Axis

GraphPlotting/graph_fixed_x_range.png

The following images show a sample of an ECG application, where eight data sources are displayed vertically tiled and auto-scaled.

GraphPlotting/sample_2.jpg

Using the Code

The control is very simple to use. Just have a look at the sample application. The following code shows how the part in the demo application where the graphs for the different examples are generated:

C#
protected void CalcDataGraphs()
{

    this.SuspendLayout();

    display.DataSources.Clear();
    display.SetDisplayRangeX(0, 400);

    for (int j = 0; j < NumGraphs; j++)
    {
        display.DataSources.Add(new DataSource());
        display.DataSources[j].Name = "Graph " + (j + 1);
        display.DataSources[j].OnRenderXAxisLabel += RenderXLabel;

        switch (CurExample)
        {
            case  "NORMAL":
                this.Text = "Normal Graph";
                display.DataSources[j].Length = 5800;
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.NORMAL;
                display.DataSources[j].AutoScaleY = false;
                display.DataSources[j].SetDisplayRangeY(-300, 300);
                display.DataSources[j].SetGridDistanceY(100);
                display.DataSources[j].OnRenderYAxisLabel = RenderYLabel;
                CalcSinusFunction_0(display.DataSources[j], j);
                break;

            case "NORMAL_AUTO":
                this.Text = "Normal Graph Autoscaled";
                display.DataSources[j].Length = 5800;
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.NORMAL;
                display.DataSources[j].AutoScaleY = true;
                display.DataSources[j].SetDisplayRangeY(-300, 300);
                display.DataSources[j].SetGridDistanceY(100);
                display.DataSources[j].OnRenderYAxisLabel = RenderYLabel;
                CalcSinusFunction_0(display.DataSources[j], j);
                break;

            case "STACKED":
                this.Text = "Stacked Graph";
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.STACKED;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = false;
                display.DataSources[j].SetDisplayRangeY(-250, 250);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_1(display.DataSources[j], j);
                break;

            case "VERTICAL_ALIGNED":
                this.Text = "Vertical aligned Graph";
                display.PanelLayout =
                    PlotterGraphPaneEx.LayoutMode.VERTICAL_ARRANGED;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = false;
                display.DataSources[j].SetDisplayRangeY(-300, 300);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_2(display.DataSources[j], j);
                break;

            case "VERTICAL_ALIGNED_AUTO":
                this.Text = "Vertical aligned Graph autoscaled";
                display.PanelLayout =
                    PlotterGraphPaneEx.LayoutMode.VERTICAL_ARRANGED;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = true;
                display.DataSources[j].SetDisplayRangeY(-300, 300);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_2(display.DataSources[j], j);
                break;

            case "TILED_VERTICAL":
                this.Text = "Tiled Graphs (vertical prefered)";
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_VER;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = false;
                display.DataSources[j].SetDisplayRangeY(-300, 600);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_2(display.DataSources[j], j);
                break;

            case "TILED_VERTICAL_AUTO":
                this.Text = "Tiled Graphs (vertical prefered) autoscaled";
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_VER;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = true;
                display.DataSources[j].SetDisplayRangeY(-300, 600);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_2(display.DataSources[j], j);
                break;

            case "TILED_HORIZONTAL":
                this.Text = "Tiled Graphs (horizontal prefered)";
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_HOR;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = false;
                display.DataSources[j].SetDisplayRangeY(-300, 600);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_2(display.DataSources[j], j);
                break;

            case "TILED_HORIZONTAL_AUTO":
                this.Text = "Tiled Graphs (horizontal prefered) autoscaled";
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_HOR;
                display.DataSources[j].Length = 5800;
                display.DataSources[j].AutoScaleY = true;
                display.DataSources[j].SetDisplayRangeY(-300, 600);
                display.DataSources[j].SetGridDistanceY(100);
                CalcSinusFunction_2(display.DataSources[j], j);
                break;

            case "ANIMATED_AUTO":

                this.Text = "Animated graphs fixed x range";
                display.PanelLayout = PlotterGraphPaneEx.LayoutMode.TILES_HOR;
                display.DataSources[j].Length = 402;
                display.DataSources[j].AutoScaleY = false;
                display.DataSources[j].AutoScaleX = true;
                display.DataSources[j].SetDisplayRangeY(-300, 500);
                display.DataSources[j].SetGridDistanceY(100);
                display.DataSources[j].XAutoScaleOffset = 50;
                CalcSinusFunction_3(display.DataSources[j], j, 0);
                display.DataSources[j].OnRenderYAxisLabel = RenderYLabel;
                break;
        }
    }

    ApplyColorSchema();

    this.ResumeLayout();
    display.Refresh();

}

The functions CalcSinusFunction_0 CalcSinusFunction_1 and CalcSinusFunction_2 are used to calculate the different sinus functions for the graphs:

C#
protected void CalcSinusFunction_0(DataSource src, int idx)
{
    for (int i = 0; i  < src.Length; i++)
    {
        src.Samples[i].x = i;
        src.Samples[i].y = (float)(((float)200 * Math.Sin((idx + 1) *(
            i + 1.0) * 48 / src.Length)));
    }    
}

The functions RenderYLabel and RenderXLabel are used to render the X and Y legends of the graph.

C#
private String RenderXLabel(DataSource s, int idx)
{
    if (s.AutoScaleX)
    {         
        int Value = (int)(s.Samples[idx].x );
        return "" + Value;     
    }
    else
    {
        int Value = (int)(s.Samples[idx].x / 200);
        String Label = "" + Value + "\"";
        return Label;
    }    
}

private String RenderYLabel(DataSource s, float value)
{ 
    return String.Format("{0:0.0}", value);
}

Summary

There are lots of parameters that can be twisted which are not explained here - just look at the code. This library is far from being finished, but it is a good point to start from. The code is simple and self-explaining. From here, it will be simple to adopt the code for your needs.

I got so much inputs from here, and I wanted to give something back. So, here is my first article on The Code Project. I hope you like it.

To Do

  • code clean ups.

Version History

  • 04.09.2014 - First update in 4 years. Found back into my code ;-) Upgraded solution to VS 2013 Express.  Cleaned up some magic numbers. More to follow soon ;-)
  • 12.07.2009 - Updated article.
  • 12.02.2009 - Implemented x autoscaling.
  • 28.01.2009 - Some more cleanups. Implemented Print Form.
  • 27.01.2009 - New display mode, some cleanups.
  • 25.01.2009 - Initial release.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Austria Austria
I have started programming at the age of 13 on the commodore 64.

Ever since then I have been programming on many systems in many languages.

During the last 12 years I have been working as professional programmer in different companies and different areas.

Now I am working as freelancer programmer / consultant

Comments and Discussions

 
GeneralMy vote of 5 Pin
nekimiro30-May-23 2:48
nekimiro30-May-23 2:48 
QuestionSpecific version of graphlib Pin
marco40229-Apr-21 22:33
marco40229-Apr-21 22:33 
Questionvisual debugger Pin
Member 150899144-Mar-21 0:16
Member 150899144-Mar-21 0:16 
PraiseGreat work. Pin
Evgeni Primakov20-Jul-20 12:04
Evgeni Primakov20-Jul-20 12:04 
QuestionC# Graph plotter Pin
Member 1205884529-Apr-19 3:08
Member 1205884529-Apr-19 3:08 
QuestionX-axis is too far Pin
Taha Temuri24-Feb-19 4:35
Taha Temuri24-Feb-19 4:35 
QuestionProblem display data from 0 to 1 Pin
Member 1362143825-Nov-18 21:22
Member 1362143825-Nov-18 21:22 
Generalthank you Pin
Farzin Sotoodi3-Nov-18 19:20
Farzin Sotoodi3-Nov-18 19:20 
QuestionHow can i use Only one scale for 5 graphs Pin
Matze4821-Feb-18 20:31
Matze4821-Feb-18 20:31 
Questionregarding plotting of data points Pin
Member 1362407417-Feb-18 0:45
Member 1362407417-Feb-18 0:45 
QuestionVB .NET Example Pin
patolucasX2719-Jan-18 5:56
patolucasX2719-Jan-18 5:56 
GeneralMy vote of 5 Pin
kaushikbm13-Dec-17 0:03
kaushikbm13-Dec-17 0:03 
QuestionLooking to hire freelancer to use this code Pin
Member 135098548-Nov-17 0:14
Member 135098548-Nov-17 0:14 
Questioni want to do something like this but i have dynamic date and i want to display that data into line graph till my software is running or with the condition.Please help me Pin
Member 1321093323-Aug-17 10:39
Member 1321093323-Aug-17 10:39 
this is my class verticalforces.cs
namespace VistaBalanceAlgorithms
{
    public class VerticalForce
    {
        public static float SteadyStateTotalForce = 1;
        public static bool SteadyState = false;

        public VerticalForce(float[] frameValues)
        {
            Left = 0;
            Right = 0;
            Total = 0;

            if (frameValues != null)
            {
                float leftForce = frameValues.Take(frameValues.Length / 2).Sum();
                float rightForce = frameValues.Skip(frameValues.Length / 2).Sum();
                Left = (int)((leftForce / SteadyStateTotalForce) * 100);
                Right = (int)((rightForce / SteadyStateTotalForce) * 100);

                Left = Left < 0 ? 0 : Left;
                Right = Right < 0 ? 0 : Right;

                Total = Left + Right;
            }
        }

        public int Left { get; protected set; }
        public int Right { get; protected set; }
        public int Total { get; protected set; }
    }

    public class VerticalForceGraph
    {
        private List<VerticalForce> vtfList = new List<VerticalForce>();

        public int BufferSize { get; set; }

        public void Add(VerticalForce vtf)
        {
            vtfList.Add(vtf);
        }

        public void Reset() { vtfList.Clear(); }

        public int Count {get{return vtfList.Count;} }

        public float[] GetLeftVTF()
        {
            return vtfList.Select(x => (float)x.Left).ToArray();
        }

        public float[] GetLeftVTF(int startPoint, int count)
        {
            return vtfList.GetRange(startPoint, count).Select(x => (float)x.Left).ToArray();
        }

        public float[] GetRightVTF()
        {
            return vtfList.Select(x => (float)x.Right).ToArray();
        }

        public float[] GetRightVTF(int startPoint, int count)
        {
            return vtfList.GetRange(startPoint, count).Select(x => (float)x.Right).ToArray();
        }

        public float[] GetTotalVTF()
        {
            return vtfList.Select(x => (float)x.Total).ToArray();
        }

        public float[] GetTotalVTF(int startPoint, int count)
        {
            return vtfList.GetRange(startPoint, count).Select(x => (float)x.Total).ToArray();
        }
    }
}




and i want to cal these function inside this method




private void VtfGraphicalViewMode(Graphics graphics)
        {
            Pen yellowPen = new Pen(Color.Yellow, 2);
            Pen bluePen = new Pen(Color.Blue, 2);
            Pen redPen = new Pen(Color.Red, 2);
            int hOffset = heatmapWidth / 2;
            int vOffset = heatmapHeight / 2;
            //Display title
            using (Font title_font = new Font("Times New Roman", 20))
            {
               
                 using (StringFormat string_format = new StringFormat())
                 {
                    string_format.Alignment = StringAlignment.Center;
                    string_format.LineAlignment = StringAlignment.Center;
                    Point title_center = new Point(this.heatmapWidth / 2, 20);
                    Point titleleft = new Point(this.heatmapHeight / 4, heatmapWidth / 10);


                    Font leftfornt = new Font("Times New Roman", 10);
                    Font totalfont = new Font("Times New Roman", 10);
                    Font rightfont = new Font("Times New Roman", 10);



                    graphics.SmoothingMode = SmoothingMode.AntiAlias;
                    graphics.DrawRectangle(outline, new Rectangle(heatmapCoord_X, heatmapCoord_Y, heatmapWidth, heatmapHeight));
                    Rectangle graph_area = new Rectangle(heatmapCoord_X, heatmapCoord_Y, heatmapWidth, heatmapHeight);
                    graphics.FillRectangle(Brushes.LightBlue, graph_area);
                    graphics.DrawString("VTF Grahical View",
                           title_font, Brushes.Red,
                           title_center, string_format);
                    graphics.DrawString("Left VTF", leftfornt, Brushes.Red, titleleft, string_format);


                    //Y-axis
                    int xOffset = 50;
                    int yOffset = 20;
                    graphics.DrawLine(redPen,
                                    heatmapCoord_X + xOffset, heatmapCoord_Y + (heatmapHeight - 50), heatmapCoord_X + xOffset, heatmapCoord_Y + yOffset
                                   );


                    //X-axis
                    graphics.DrawLine(redPen,
                                    heatmapCoord_X + (heatmapWidth - 10), heatmapCoord_Y + (heatmapHeight - 50), heatmapCoord_X + xOffset, heatmapCoord_Y + (heatmapHeight - 50)
                                   );




                }
            }
        }

QuestionReal time graph plotting Pin
Kanjah7723-May-17 11:50
Kanjah7723-May-17 11:50 
QuestionRange in the left part of the window Pin
Dod7415-Sep-16 4:27
Dod7415-Sep-16 4:27 
AnswerRe: Range in the left part of the window Pin
Dod7427-Sep-16 23:27
Dod7427-Sep-16 23:27 
QuestionHow can I plot my data using this library? Which is the data entry point in it? PinPopular
KittoKy20-Mar-16 11:48
KittoKy20-Mar-16 11:48 
AnswerRe: How can I plot my data using this library? Which is the data entry point in it? Pin
Member 944370226-Apr-17 20:25
Member 944370226-Apr-17 20:25 
QuestionVery Nice.....Measurement cursor? Pin
riscy17-Feb-16 10:19
riscy17-Feb-16 10:19 
QuestionGraph plotting Pin
Reggie Beneke7-Feb-16 8:29
Reggie Beneke7-Feb-16 8:29 
GeneralMy vote of 5 Pin
David D12-Nov-15 8:14
David D12-Nov-15 8:14 
QuestionPlotting contentiously random data in C# Pin
Member 120422118-Oct-15 22:01
Member 120422118-Oct-15 22:01 
QuestionReal Time Graph Plotting Pin
Member 115832257-Oct-15 19:25
Member 115832257-Oct-15 19:25 
QuestionUsing control in a C# project Pin
robiwano6-Aug-15 2:39
robiwano6-Aug-15 2:39 

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

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