使用C#和WinForms创建动态图表的两种方法

 更新时间:2025年09月26日 09:58:36   作者:oh-caiii  
本文详细介绍了如何使用C#和WinForms中的Chart控件创建动态图表,包括实例化图表、添加数据点、使用多线程实现实时更新,并提供两种方法创建图表实例,需要的朋友可以参考下

前言

这次我们将学习如何使用 C# 和 WinForms 创建动态图表。我们将使用 Chart 控件来创建图表,并使用多线程技术实现动态更新图表数据的效果。

方法一:在项目启动时实例化图表

在 DoWork 方法中,我们使用 Random 类生成随机数作为 Y 值,并使用 Series 对象的 Points 集合来添加数据点。如果数据点数量超过 20 条,我们将移除最旧的数据点,并更新所有数据点的 X 坐标值,以确保仅显示最新的 20 条数据。

  1. 初始化图表

在窗体的构造函数中,创建一个 Chart 控件,并将其 Dock 属性设置为 Fill,以使其占据整个窗体的空间。然后创建一个 ChartArea,并将其添加到 Chart 控件上。接着创建两个 Series(系列),并将它们都添加到 Chart 控件上。最后将 Chart 控件的 BorderSkin.SkinStyle 属性设置为 None,以隐藏外部边框。

  // 初始化图表
            chart1 = new Chart
            {
                Parent = this,
                Dock = DockStyle.Fill
            };

            // 创建一个新的图表区域
            ChartArea chartArea1 = new ChartArea();
            chart1.ChartAreas.Add(chartArea1);

            // 设置图表区域的边框颜色为透明
            chartArea1.BorderColor = Color.FromArgb(255, 0, 0);

            // 创建俩个折线图系列
            series1 = new Series
            {
                ChartType = SeriesChartType.Line
            };
            series2 = new Series
            {
                ChartType = SeriesChartType.Line
            };

            // 将系列添加到图表上
            chart1.Series.Add(series1);
            chart1.Series.Add(series2);

            // 隐藏图表的外部边框
            chart1.BorderSkin.SkinStyle = BorderSkinStyle.None;
  1. 点击按钮后增加数据点

当用户点击“开始”按钮时,会触发 Button1_Click 事件处理程序。在该事件处理程序中,创建一个新线程并调用 DoWork 方法,在该方法中不断地添加新数据点。每次添加新数据点时,先生成一个随机数作为 Y 坐标值,然后调用 BeginInvoke 方法,将更新 UI 元素的代码封装在一个 Action 委托中,并将该委托传递给 BeginInvoke 方法。这样可以确保 UI 元素的更新操作是异步执行的,从而避免阻塞 UI 界面。

        private void Button1_Click(object sender, EventArgs e)
        {
            // 创建新线程
            Thread thread = new Thread(new ThreadStart(DoWork));
            thread.Start();
        }
  1. 更新数据点的 X 坐标值

如果数据点的数量超过了20个,就需要移除最旧的数据点。移除操作后,需要更新剩余数据点的 X 坐标值,确保仅显示最新的20个数据点。这是通过循环遍历所有数据点并更新其 X 坐标值实现的。

  1. 刷新图表

每次添加新数据点后,需要调用 chart1.DataBind() 方法刷新图表,并将 xValue 的值自增1,以便下一次添加新数据点时能够使用正确的 X 坐标值。

 private void DoWork()
        {
            while (true)
            {
                // 检查窗体是否已经关闭
                if (IsDisposed || Disposing)
                {
                    return;
                }
                // 每次点击按钮时,增加X值和随机Y值
                Random random = new Random();
                int yValue1 = random.Next(50, 140);
                int yValue2 = random.Next(50, 140);
                // 异步更新 UI 元素
                BeginInvoke(new Action(() =>
                {
                    // 更新 UI 元素的代码
                    series1.Points.AddXY(xValue, yValue1);
                    series2.Points.AddXY(xValue, yValue2);

                    if (series1.Points.Count > 20)
                    {
                        // 如果数据点数量超过100条,移除最旧的数据点
                        series1.Points.RemoveAt(0);

                        // 更新所有数据点的X坐标值,确保仅显示最新的100条数据
                        for (int i = 0; i < series1.Points.Count; i++)
                        {
                            series1.Points[i].XValue = i + 1;
                        }
                    }
                    if (series2.Points.Count > 20)
                    {
                        // 如果数据点数量超过100条,移除最旧的数据点
                        series2.Points.RemoveAt(0);

                        // 更新所有数据点的X坐标值,确保仅显示最新的100条数据
                        for (int i = 0; i < series2.Points.Count; i++)
                        {
                            series2.Points[i].XValue = i + 1;
                        }
                    }
                    // 更新X值
                    xValue++;

                    // 刷新图表
                    chart1.DataBind();
                }));

                Thread.Sleep(500);
            }
        }

全部代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private readonly Chart chart1;
        private readonly Series series1;
        private readonly Series series2;
        private int xValue = 1; // 初始X值

        public Form1()
        {
            InitializeComponent();

            // 初始化图表
            chart1 = new Chart
            {
                Parent = this,
                Dock = DockStyle.Fill
            };

            // 创建一个新的图表区域
            ChartArea chartArea1 = new ChartArea();
            chart1.ChartAreas.Add(chartArea1);

            // 设置图表区域的边框颜色为透明
            chartArea1.BorderColor = Color.FromArgb(255, 0, 0);

            // 创建俩个折线图系列
            series1 = new Series
            {
                ChartType = SeriesChartType.Line
            };
            series2 = new Series
            {
                ChartType = SeriesChartType.Line
            };

            // 将系列添加到图表上
            chart1.Series.Add(series1);
            chart1.Series.Add(series2);

            // 隐藏图表的外部边框
            chart1.BorderSkin.SkinStyle = BorderSkinStyle.None;
        }
        //需要在From1窗体添加一个名为Button1的按钮 一个名为Button1的方法
        private void Button1_Click(object sender, EventArgs e)
        {
            // 创建新线程
            Thread thread = new Thread(new ThreadStart(DoWork));
            thread.Start();
        }

        private void DoWork()
        {
            //循环无限添加点 形成折线图
            while (true)
            {
                // 检查窗体是否已经关闭
                if (IsDisposed || Disposing)
                {
                    return;
                }
                // 每次点击按钮时,增加X值和随机Y值
                Random random = new Random();
                int yValue1 = random.Next(50, 140);
                int yValue2 = random.Next(50, 140);
                // 异步更新 UI 元素
                BeginInvoke(new Action(() =>
                {
                    // 更新 UI 元素的代码
                    series1.Points.AddXY(xValue, yValue1);
                    series2.Points.AddXY(xValue, yValue2);

                    if (series1.Points.Count > 20)
                    {
                        // 如果数据点数量超过100条,移除最旧的数据点
                        series1.Points.RemoveAt(0);

                        // 更新所有数据点的X坐标值,确保仅显示最新的100条数据
                        for (int i = 0; i < series1.Points.Count; i++)
                        {
                            series1.Points[i].XValue = i + 1;
                        }
                    }
                    if (series2.Points.Count > 20)
                    {
                        // 如果数据点数量超过100条,移除最旧的数据点
                        series2.Points.RemoveAt(0);

                        // 更新所有数据点的X坐标值,确保仅显示最新的100条数据
                        for (int i = 0; i < series2.Points.Count; i++)
                        {
                            series2.Points[i].XValue = i + 1;
                        }
                    }
                    // 更新X值
                    xValue++;

                    // 刷新图表
                    chart1.DataBind();
                }));

                Thread.Sleep(500);
            }
        }
    }
}

运行结果

在 Form1 构造函数中,我们初始化了图表控件,创建了一个新的图表区域和两个折线图系列,并将这些元素添加到图表控件上。

在运行程序后,当用户点击按钮时,图表控件将开始显示动态的折线图,随着时间的推移,新的数据点将加入图表中,并移除最旧的数据点。

这是直接在页面打开的时候进行初始化 一个Chart图表 ,和俩条Series折线图Line

方法二:拖动控件的方法创建图表

工具箱的位置

步骤一

输入Chart 将 Chart拖动到From1上

步骤二

打开Chart的属性,找到属性Series,打开集合添加成员类型(series就是一个图表),找到左侧的图表属性ChartType,选择Line。这句话等效于如下代码。

// 创建俩个折线图系列
series1 = new Series
{
    ChartType = SeriesChartType.Line
};
series2 = new Series
{
    ChartType = SeriesChartType.Line
};

步骤三

完成上面步骤最后应该是如下图

全部代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
       
        private int xValue = 1; // 初始X值

        public Form1()
        {
            InitializeComponent();  
        }

        private void button2_Click(object sender, EventArgs e)
        {
            // 创建新线程
            Thread thread = new Thread(new ThreadStart(DoWork));
            thread.Start();
        }

        private void DoWork()
        {
            //将俩条折线取出来
            Series series1 = chart2.Series[0];
            Series series2 = chart2.Series[1];

            while (true)
            {
                // 检查窗体是否已经关闭
                if (IsDisposed || Disposing)
                {
                    return;
                }
                // 每次点击按钮时,增加X值和随机Y值
                Random random = new Random();
                int yValue1 = random.Next(50, 140);
                int yValue2 = random.Next(50, 140);
                // 异步更新 UI 元素
                BeginInvoke(new Action(() =>
                {
                    // 更新 UI 元素的代码
                    series1.Points.AddXY(xValue, yValue1);
                    series2.Points.AddXY(xValue, yValue2);

                    if (series1.Points.Count > 20)
                    {
                        // 如果数据点数量超过100条,移除最旧的数据点
                        series1.Points.RemoveAt(0);

                        // 更新所有数据点的X坐标值,确保仅显示最新的100条数据
                        for (int i = 0; i < series1.Points.Count; i++)
                        {
                            series1.Points[i].XValue = i + 1;
                        }
                    }
                    if (series2.Points.Count > 20)
                    {
                        // 如果数据点数量超过100条,移除最旧的数据点
                        series2.Points.RemoveAt(0);

                        // 更新所有数据点的X坐标值,确保仅显示最新的100条数据
                        for (int i = 0; i < series2.Points.Count; i++)
                        {
                            series2.Points[i].XValue = i + 1;
                        }
                    }
                    // 更新X值
                    xValue++;

                    // 刷新图表
                    chart2.DataBind();
                }));

                Thread.Sleep(500);
            }
        }
    }
}

运行结果

扩展

以下是一些常见的 chart2.Series 属性和方法的使用示例:

//添加一个新系列:
Series newSeries = new Series();
chart2.Series.Add(newSeries);

//删除指定索引位置的系列:
chart2.Series.RemoveAt(index);

//获取指定索引位置的系列:
Series series = chart2.Series[index];

//遍历所有系列:
foreach (Series series in chart2.Series)
{
    // 对每个系列执行操作
}

//设置系列的属性,如图表类型和系列名称:
series.ChartType = SeriesChartType.Line;
series.Name = "Series Name";

//清空所有系列:
chart2.Series.Clear();

注意事项

必须用线程来启动这个方法,异步更新 UI 元素,防止动态卡死。

以上就是使用C#和WinForms创建动态图表的两种方法的详细内容,更多关于C# WinForms创建动态图表的资料请关注脚本之家其它相关文章!

相关文章

  • C#实现的pdf生成图片文字水印类实例

    C#实现的pdf生成图片文字水印类实例

    这篇文章主要介绍了C#实现的pdf生成图片文字水印类,结合完整实例形式分析了C#针对pdf文件的创建、添加文字、水印等相关操作技巧,需要的朋友可以参考下
    2017-09-09
  • 深入理解C#中foreach遍历的使用方法

    深入理解C#中foreach遍历的使用方法

    在c#中通过foreach遍历一个列表是经常拿用的方法,使用起来也方便,下面这篇文章先给大家介绍了关于C#中foreach遍历的使用方法,后面介绍了c#使用foreach注意的一些是,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-08-08
  • C#中变量、常量、枚举、预处理器指令知多少

    C#中变量、常量、枚举、预处理器指令知多少

    这篇文章主要介绍了c#共有其中变量类型有:静态变量、实类变量、数组元素、数值参数、引用参数、输出参数和局部变量,需要的朋友可以参考一下
    2017-04-04
  • 关于C#中yield return用法的思考

    关于C#中yield return用法的思考

    在这篇文章中,我们将深入讨论 C# 中yield return的机制和用法,帮助您更好地理解这个强大的功能,并在实际开发中灵活使用它,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-05-05
  • C#删除最后一个结尾逗号的方法

    C#删除最后一个结尾逗号的方法

    这篇文章主要介绍了C#删除最后一个结尾逗号的方法,涉及C#、操作字符串的技巧,简单实用,非常具有实用价值,需要的朋友可以参考下
    2015-03-03
  • UGUI实现ScrollView无限滚动效果

    UGUI实现ScrollView无限滚动效果

    这篇文章主要为大家详细介绍了UGUI实现ScrollView无限滚动效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • C#数据绑定(DataBinding)简单实现方法

    C#数据绑定(DataBinding)简单实现方法

    这篇文章主要介绍了C#数据绑定(DataBinding)简单实现方法,以简单实例形式简单分析了C#实现数据绑定与读取的方法,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-08-08
  • C#验证给定字符串是否为数字的方法

    C#验证给定字符串是否为数字的方法

    这篇文章主要介绍了C#验证给定字符串是否为数字的方法,实例分析了C#进行字符串操作的技巧,非常具有实用价值,需要的朋友可以参考下
    2015-03-03
  • Unity Shader实现2D游戏迷雾

    Unity Shader实现2D游戏迷雾

    这篇文章主要为大家详细介绍了Unity Shader实现2D游戏迷雾,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • C# 多窗口委托通信的实现

    C# 多窗口委托通信的实现

    本文主要介绍了C# 多窗口委托通信的实现,窗口之间通信无非有两个方向,主窗口发送数据到副窗口,副窗口发送数据到主窗口,感兴趣的可以了解一下
    2022-03-03

最新评论