chart control dynamic curve x-axis time ms display, scrollable, add boundary lines

Reference documentation:

1. How to display time on the x-axis

The x-axis of c# chart sets the time format, the first coordinate starts from 0_c# chart coordinate axis-CSDN blog

2. Dynamic curve drawing (can be partially enlarged)

C# winform multiple charts dynamic curve drawing (can be partially enlarged)_winform curve chart-CSDN blog

3. When the amount of data is large, a scroll bar appears on the x-axis

Chart control in C#–(When the amount of data is large, a scroll bar appears on the x-axis)_winform chart x-axis millisecond scroll bar-CSDN Blog

4. Add border lines

c# chart control adds boundary value lines and scalability functions – AKA-Green – Blog Park (cnblogs.com)

Combined with the above reference, the x-axis is set to ms and the scroll bar can be dragged, supports zooming in and out, and supports maximum value display.

X axis type setting

//interval
            //chartArea.AxisY.Interval = 20;
            //chartArea.AxisY.Minimum = 0;
            //chartArea.AxisY.Maximum = 100;

            chartArea.AxisX.LabelStyle.Format = "yy-MM-dd\r\\
HH:mm:ss.fff"; //abscissa format
            chartArea.AxisX.IntervalType = DateTimeIntervalType.Milliseconds;//If it is time type data, the interval mode can be seconds, minutes, hours
            //chartArea.AxisX.Minimum = dateTime.ToOADate();
            //chartArea.AxisX.Maximum = dateTime.AddMilliseconds(1000).ToOADate();
            chartArea.AxisX.Interval = DateTime.Parse("00:00:00.100").Millisecond;

Pay special attention to the settings of the data view, otherwise the mouse will not be able to drag the scroll axis.

//Data view
            chartArea.AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.All;//Enable the X-axis scroll bar button
            chartArea.AxisX.ScaleView.SizeType = DateTimeIntervalType.Auto;
            chartArea.AxisX.ScaleView.MinSize = 100;
            chartArea.AxisX.ScaleView.MinSizeType = DateTimeIntervalType.Milliseconds;
            chartArea.AxisX.ScaleView.SmallScrollMinSize = 100;
            chartArea.AxisX.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Milliseconds;
            chartArea.AxisX.ScaleView.SmallScrollSize = 100;
            chartArea.AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Milliseconds;
            chartArea.AxisX.ScaleView.Zoomable = true;

The type is set to ms. If the unit of the scroll axis is too large by default, the interface will display in ms and the scroll axis will not be dragged.

To realize the scroll bar following the curve in real time, you mainly need to set the value of chart1.ChartAreas[0].AxisX.ScaleView.Position.

The maximum number of data displayed on a page is determined by chart1.ChartAreas[0].AxisX.ScaleView.Size, combined with chartArea.AxisX.Interval = DateTime.Parse(“00:00:00.100”).Millisecond; set in the interval; The parameters can be used to calculate how many data a page can display.

range = r.Next(1, 60); //Randomly pick a number

                dateTime = dateTime.AddMilliseconds(100);
                series1.Points.AddXY(dateTime, 5 + range); //Set series points

                chart1.ChartAreas[0].AxisX.ScaleView.Size = dateTime.AddMilliseconds(900).ToOADate() - dateTime.ToOADate();
                sum + + ;
                if (sum <= chart1.ChartAreas[0].AxisX.ScaleView.Size)
                    chart1.ChartAreas[0].AxisX.ScaleView.Position = dateTime.ToOADate();
                else
                    chart1.ChartAreas[0].AxisX.ScaleView.Position = dateTime.ToOADate() - chart1.ChartAreas[0].AxisX.ScaleView.Size;

overall code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using ToolTip = System.Windows.Forms.ToolTip;

namespace TemperDisplay
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            createSeries();
            CreateChart();
        }

        private void CreateChart()
        {
            ChartArea chartArea = chart1.ChartAreas[0];
            dateTime = DateTime.Now;

            //cursor
            chartArea.CursorX.IsUserEnabled = true;
            chartArea.CursorX.IsUserSelectionEnabled = true;
            chartArea.CursorX.AutoScroll = true;
            chartArea.CursorX.SelectionColor = Color.SkyBlue;
            chartArea.CursorX.IntervalType = DateTimeIntervalType.Milliseconds;
            chartArea.CursorX.Interval = 1;
            chartArea.CursorX.AxisType = AxisType.Primary;

            //Data view
            chartArea.AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.All;//Enable the X-axis scroll bar button
            chartArea.AxisX.ScaleView.SizeType = DateTimeIntervalType.Auto;
            chartArea.AxisX.ScaleView.MinSize = 100;
            chartArea.AxisX.ScaleView.MinSizeType = DateTimeIntervalType.Milliseconds;
            chartArea.AxisX.ScaleView.SmallScrollMinSize = 100;
            chartArea.AxisX.ScaleView.SmallScrollMinSizeType = DateTimeIntervalType.Milliseconds;
            chartArea.AxisX.ScaleView.SmallScrollSize = 100;
            chartArea.AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Milliseconds;
            chartArea.AxisX.ScaleView.Zoomable = true;

            //Exterior
            chartArea.BackColor = Color.AliceBlue; //Background color
            chartArea.BackSecondaryColor = Color.White; //Gradient background color
            chartArea.BackGradientStyle = GradientStyle.TopBottom; //Gradient mode
            chartArea.BackHatchStyle = ChartHatchStyle.None; //Background shadow
            chartArea.BorderDashStyle = ChartDashStyle.NotSet; //Border line style
            chartArea.BorderWidth = 1; //Border width
            chartArea.BorderColor = Color.Black;

            //Dividing line
            chartArea.AxisX.MajorGrid.Enabled = true;
            chartArea.AxisY.MajorGrid.Enabled = true;

            // Axis tag
            chartArea.AxisY.Title = @"Value";
            chartArea.AxisY.LabelAutoFitMinFontSize = 5;
            
            
            chartArea.AxisX.Title = @"Time";
            chartArea.AxisX.IsLabelAutoFit = true;
            chartArea.AxisX.LabelAutoFitMinFontSize = 5;
            chartArea.AxisX.LabelStyle.Angle = 0; //Label rotation angle -90 --- 90
            chartArea.AxisX.LabelStyle.IsEndLabelVisible = true;
            chartArea.AxisX.TextOrientation = TextOrientation.Auto; //Text direction

            //interval
            //chartArea.AxisY.Interval = 20;
            //chartArea.AxisY.Minimum = 0;
            //chartArea.AxisY.Maximum = 100;

            chartArea.AxisX.LabelStyle.Format = "yy-MM-dd\r\\
HH:mm:ss.fff"; //abscissa format
            chartArea.AxisX.IntervalType = DateTimeIntervalType.Milliseconds;//If it is time type data, the interval mode can be seconds, minutes, hours
            //chartArea.AxisX.Minimum = dateTime.ToOADate();
            //chartArea.AxisX.Maximum = dateTime.AddMilliseconds(1000).ToOADate();
            chartArea.AxisX.Interval = DateTime.Parse("00:00:00.100").Millisecond;
            
            //Exterior
            chartArea.AxisY.LineWidth = 2; //Vertical edge width of data table
            chartArea.AxisY.LineColor = Color.Black; //Color of vertical edge of data table
            chartArea.AxisY.Enabled = AxisEnabled.True;

            chartArea.AxisX.LineWidth = 2;
            chartArea.AxisX.LineColor = Color.Black;
            chartArea.AxisX.Enabled = AxisEnabled.True;

            //Auxiliary lines
            double max = 25;
            StripLine stripMax = new StripLine();
            stripMax.Text = string.Format("Maximum: {0:F}", max);//Display text
            stripMax.BackColor = Color.FromArgb(208, 109, 106);//Background color
            stripMax.Interval = 0;//interval
            stripMax.IntervalOffset = max;//Offset
            stripMax.StripWidth = 0.001;//Line width
            stripMax.ForeColor = Color.Yellow;//Foreground color
            stripMax.TextAlignment = StringAlignment.Near;//Text alignment
            chartArea.AxisY.StripLines.Add(stripMax);//Add to ChartAreas

            //Chart area rectangular position
            //chartArea.Position.Height = 85;
            //chartArea.Position.Width = 85;
            //chartArea.Position.X = 5;
            //chartArea.Position.Y = 7;

            //Gradient style
            chart1.BackGradientStyle = GradientStyle.TopBottom;
            //Chart border color,
            chart1.BorderlineColor = Color.FromArgb(26, 59, 105);
            //Chart border line style
            chart1.BorderlineDashStyle = ChartDashStyle.Solid;
            //The width of the chart border line
            chart1.BorderlineWidth = 2;
            //The skin of the chart border
            chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;

        }
        Series series1;

        private void createSeries()
        {
            //Series1
            series1 = chart1.Series[0];

            //Series1 style
            series1.ToolTip = "Temperature:#VALY{F1}\\
Time:#VALX{HH-mm-dd.fff}"; //The mouse stays on the data point and the XY value is displayed

            series1.Name = "PT1000 temperature";
            series1.ChartType = SeriesChartType.Spline; // type
            series1.BorderWidth = 2; //Line width
            series1.Color = Color.Red; //Line color
            series1.XValueType = ChartValueType.DateTime; //Display time
            series1.YValueType = ChartValueType.Auto;
            series1.IsValueShownAsLabel = true; //Display data value
            series1.LabelFormat = "F1"; //Display data format
            series1.LabelForeColor = Color.Black; //Display data color
            series1.LabelBackColor = Color.AliceBlue; //Display data background color

            //Marker
            series1.MarkerStyle = MarkerStyle.Square; //Label point style
            series1.MarkerSize = 5;
            series1.MarkerColor = Color.Black; //label point color

            //icon legend
            //Dock
            chart1.Legends[0].Alignment = StringAlignment.Far; //To the right
            chart1.Legends[0].Docking = Docking.Top; //Towards the top
        }

        static int range = 0;
        Random r = new Random(6);

        int sum = 1;
        bool flag = false;
        DateTime dateTime;
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (flag)
                return;
            else
            {
              
                range = r.Next(1, 60); //Randomly pick a number

                dateTime = dateTime.AddMilliseconds(100);
                series1.Points.AddXY(dateTime, 5 + range); //Set series points

                chart1.ChartAreas[0].AxisX.ScaleView.Size = dateTime.AddMilliseconds(900).ToOADate() - dateTime.ToOADate();
                sum + + ;
                if (sum <= chart1.ChartAreas[0].AxisX.ScaleView.Size)
                    chart1.ChartAreas[0].AxisX.ScaleView.Position = dateTime.ToOADate();
                else
                    chart1.ChartAreas[0].AxisX.ScaleView.Position = dateTime.ToOADate() - chart1.ChartAreas[0].AxisX.ScaleView.Size;

                //series1.Points.AddXY(sum, 5 + range);
                //sum + + ;
                //if (comboBox1.SelectedItem.ToString() == "OverView") //Switch view
                //{
                // chart1.ChartAreas[0].AxisX.ScaleView.Position = 1;
                // if (sum > 10)
                // {
                // double max = chart1.ChartAreas[0].AxisX.Maximum;
                // max = (sum / 10 + 1) * 10;
                // chart1.ChartAreas[0].AxisX.Interval = max / 10;
                // }
                // chart1.ChartAreas[0].AxisX.ScaleView.Size = sum * 1.1;
                // //chart.ChartAreas[0].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.None;//Enable the X-axis scroll bar button
                //}
                //if (comboBox1.SelectedItem.ToString() == "Follow")
                //{
                // //chart1.ChartAreas[0].AxisX.Interval = 1D;
                // chart1.ChartAreas[0].AxisX.ScaleView.Size = 10D;
                // if (sum <= chart1.ChartAreas[0].AxisX.ScaleView.Size)
                // chart1.ChartAreas[0].AxisX.ScaleView.Position = 1;
                //else
                // chart1.ChartAreas[0].AxisX.ScaleView.Position = sum - chart1.ChartAreas[0].AxisX.ScaleView.Size;
                //}
            }

        }


        private void button1_Click(object sender, EventArgs e)
        {
            if (button1.Text == "Start")
            {
                timer1.Start();
                button1.Text = "Pause";
            }
            else
            {
                timer1.Stop();
                button1.Text = "Start";
            }
        }

        private void chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
        {
            //Return when there is no data
            if (chart1.Series[0].Points.Count == 0)
                return;

            double start_position = 0.0;
            double end_position = 0.0;
            double myInterval = 0.0;
            start_position = e.NewSelectionStart;
            end_position = e.NewSelectionEnd;
            myInterval = Math.Abs(start_position - end_position);

            Console.WriteLine(start_position.ToString() + " " + end_position.ToString() + " " + myInterval.ToString());

            //if (myInterval == 0.0)
            // return;

            X-axis view starting point
            //chart1.ChartAreas[0].AxisX.ScaleView.Position = Math.Min(start_position, end_position);
            X-axis view length
            //chart1.ChartAreas[0].AxisX.ScaleView.Size = myInterval;
            X-axis interval
            //if (myInterval < dateTime.AddMilliseconds(900).ToOADate() - dateTime.ToOADate())
            //{
            // chart1.ChartAreas[0].AxisX.Interval = 100;
            //}
            //else
            //{
            // chart1.ChartAreas[0].AxisX.Interval = Math.Floor(myInterval / 10);
            //}
            //flag = true;
            //if (!comboBox1.Items.Contains("Zoom"))
            //{
            // comboBox1.Items.Add("Zoom");
            // comboBox1.SelectedItem = "Zoom";
            //}

        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox1.SelectedItem.ToString() == "Zoom")
            {
                flag = true;
            }
            else if (comboBox1.SelectedItem.ToString() == "OverView" || comboBox1.SelectedItem.ToString() == "Follow")
            {
                comboBox1.Items.Remove("Zoom");
                flag = false;
            }

        }



        Point lastPoint = new Point();//Coordinates of the last point
        ToolTip tp = new ToolTip();//tooltip display bar
        //Draw vertical line coordinates
        Point p1 = new Point(0, 0);
        Point p2 = new Point(0, 0);
        /// 
        ///Mouse move event
        /// 
        /// 
        /// 
        private void chart1_MouseMove(object sender, MouseEventArgs e)
        {
            this.Refresh();//Refresh the chart, using clear will completely clear the graphics on the chart
            Pen pen = new Pen(Color.Yellow);
            Graphics g = chart1.CreateGraphics();
            string seriesInfo = ""; //tooltip text
            if (e.Location != lastPoint)//If no operation is performed at the last point, the operation here will trigger the refresh operation of the chart control and cause the interface to flicker.
            {
                for (int y = 0; y <= chart1.Size.Height; y + + )//Line range for collision detection
                {
                    HitTestResult result = chart1.HitTest(e.X, y);
                    if (result.ChartElementType == ChartElementType.DataPoint)
                    {
                        foreach (DataPoint dpp in result.Series.Points)//Default style of data points. Occasionally, the modification cannot be completed normally when modified using the index method.
                        {
                            dpp.MarkerStyle = MarkerStyle.Diamond;
                            dpp.MarkerColor = Color.White;
                            dpp.MarkerSize = 5;
                        }
                        int i = result.PointIndex;
                        DataPoint dp = result.Series.Points[i];
                        dp.MarkerStyle = MarkerStyle.Star4;//The style of captured data points
                        dp.MarkerColor = Color.Orange;
                        dp.MarkerSize = 15;
                        //Get the relative coordinates of the data point
                        p1 = new Point((int)chart1.ChartAreas[0].AxisX.ValueToPixelPosition(dp.XValue), 0);
                        p2 = new Point((int)chart1.ChartAreas[0].AxisX.ValueToPixelPosition(dp.XValue), chart1.Size.Height);
                        seriesInfo = string.Format("Item: {0} Time: {1} Energy consumption value: {2}", result.Series.LegendText, DateTime.FromOADate(dp.XValue), dp.YValues[0] );
                        break;
                    }
                }
                tp.AutoPopDelay = 5000;//Show tooltip
                tp.ShowAlways = false;
                tp.IsBalloon = true;
                tp.SetToolTip(chart1, seriesInfo);
            }
            lastPoint = e.Location;//Record this location
            g.DrawLine(pen, p1, p2);//Draw a vertical line
        }
    }
}

Design interface

Events that need to be added

final effect

magnification effect

The auxiliary lines are not associated and can be associated according to the actual situation.

Crossing the river by feeling the stones, thanks to other bloggers for providing code, long live open source!