使用 C# 和 WinForms 创建动态图表

news2024/12/22 20:31:17

使用 C# 和 WinForms 创建动态图表

这次我们将学习如何使用 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 元素,防止动态卡死。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1368996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宝塔上的琉璃灯(for循环试炼)

8层宝塔上共有765盏琉璃灯&#xff0c;每层灯数都是上层的一倍&#xff0c;编程输出每层灯数。 (笔记模板由python脚本于2024年01月09日 16:41:22创建&#xff0c;本篇笔记适合熟悉循环编程的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python…

【教程】代码混淆详解

目录 引言 正文 什么是代码混淆 ProGuard混淆文件参数详解 代码混淆的方法 Ipa Guard工具的使用方法 IPA重签名与安装测试 总结 本文将对代码混淆进行详细解释&#xff0c;并介绍ProGuard代码混淆器以及Ipa Guard工具的使用方法。首先&#xff0c;我们将了解代码混淆的概…

ORPC-824,对标可替代ACPL-824/PC824等

提供隔离反馈 逻辑电路之间的接口 电平转换 DC和AC输入 SMPS中的调节反馈电路 消除接地环路 特征 电流传输比 &#xff08; CTR &#xff1a; 最低 20% 在 IF 1mA&#xff0c; VCE 5V &#xff09; 宽工作温度范围 -55~110C 高输入输出隔离电压 &#xff08; Viso 5&am…

【算法设计与分析】网络流

目录 max-flow 和 min-cut流网络 Flow network最小割 Min-cut最大流 Max-flow Greedy algorithmFord–Fulkerson algorithm剩余网络 Residual networkFord–Fulkerson algorithm算法流程 最大流最小割理论 max-flow min-cut theorem容量扩展算法 capacity-scaling algorithm时间…

leetcode 每日一题 2024年01月09日 字符串中的额外字符

题目 字符串中的额外字符 给你一个下标从 0 开始的字符串 s 和一个单词字典 dictionary 。你需要将 s 分割成若干个 互不重叠 的子字符串&#xff0c;每个子字符串都在 dictionary 中出现过。s 中可能会有一些 额外的字符 不在任何子字符串中。 请你采取最优策略分割 s &…

解决命令行无法启动scrapy爬虫

前言 最近在准备毕设项目&#xff0c;想使用scrapy架构来进行爬虫&#xff0c;找了一个之前写过的样例&#xff0c;没想到在用普通的启动命令时报错。报错如下 无法将“scrapy”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径…

【AI】AI和点云(1/2)

目录 一、什么是点云 二、点云的应用领域 三、点云的创建 四、点云感知 一、什么是点云 在三维技术领域中&#xff0c;点云被定义为一种数据结构&#xff0c;用于表示三维空间中一组离散的点。这些点通常由它们的坐标&#xff08;x&#xff0c;y&#xff0c;z&#xff09;…

全视通以“物联数据中台+边缘信息化”,建设“三位一体”智慧医院

随着医疗信息化的不断深入&#xff0c;智慧医院已经成为医疗行业的发展方向。智慧医院是指利用信息技术和物联网技术&#xff0c;实现医疗服务、医疗管理和医疗保障的高效协同&#xff0c;提高医疗质量和安全&#xff0c;降低医疗成本&#xff0c;满足患者和医务人员的需求&…

Java_Swing程序设计

swing组件允许编程人员在跨平台时指定统一的外观和风格。 Swing组件通常被称为轻量级组件&#xff0c; JFrame在程序中的语法格式&#xff1a; JFrame jfnew JFrame(title); Container containerjf.getContentPane(); jf:JFrame类的对象 container:Container类的对象。 J…

Flutter - Android 安卓 消息推送FireBase notification 手机状态栏图标不显示或 白板、白底问题。

一、问题&#xff1a; 使用flutterfirebase 开发进行消息推送时&#xff0c;安卓真机推送消息 状态栏的图标显示白色方块。 二、原因&#xff1a; 从Android 5.0&#xff08;Lollipop&#xff09;开始&#xff0c;随着Material Design的引入&#xff0c;Android的设计语言和U…

单因素方差分析--R

任务说明 三个剂量水平的药物处理受试者&#xff0c;每个剂量水平十个受试者&#xff0c;现在收集到数据后&#xff0c;问&#xff1a; 药物剂量水平显著影响受试者的response&#xff1f; 或者不同剂量药物处理受试者有显著效果的差异吗&#xff1f; 数据 library(tidyvers…

c JPEG编码,但有错误

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h> //v4l2 头文件 #include <strin…

3. SPSS数据文件的基本加工和处理

如何获取SPSS自带的案例数据文件&#xff1f; 首先找到SPSS的安装目录&#xff0c;然后找到Samples文件夹 可以看到有不同语言版本&#xff0c;选择简体中文 就能看到很多.sav文件 数据文件的整理 个案排序 单值排序 例&#xff1a;对于下面的数据集&#xff0c;将工资按…

拼多多API:从数据中挖掘商业价值的力量

随着大数据时代的来临&#xff0c;数据已经成为企业决策和创新的基石。拼多多API作为电商领域的重要接口&#xff0c;为企业提供了从数据中挖掘商业价值的机会。通过拼多多API&#xff0c;企业可以获取丰富的用户数据、商品数据和交易数据&#xff0c;从而深入了解市场需求、优…

【leetcode】字符串中的第一个唯一字符

题目描述 给定一个字符串 s &#xff0c;找到 它的第一个不重复的字符&#xff0c;并返回它的索引 。如果不存在&#xff0c;则返回 -1 。 用例 示例 1&#xff1a; 输入: s “leetcode” 输出: 0 示例 2: 输入: s “loveleetcode” 输出: 2 示例 3: 输入: s “aabb”…

Flink CDC使用

Flink 环境准备 Flink 版本对应的CDC版本 两个jar包上传到flink bin目录下 flink-sql-connector-mysql-cdc mysql-connector-java 重启Flink集群

跟我学java|Stream流式编程——Stream 的中间操作

书接上回&#xff0c;继续研究。 过滤操作&#xff08;filter&#xff09; 过滤操作&#xff08;filter&#xff09;是 Stream API 中的一种常用操作方法&#xff0c;它接受一个 Predicate 函数作为参数&#xff0c;用于过滤 Stream 中的元素。只有满足 Predicate 条件的元素会…

搭建Windows版Redis集群

redis集群 Redis单机版安装 链接: Redis官网下载地址 下载完成后解压至指定目录 打开一个 cmd 窗口 使用 cd 命令切换目录到 E:\Redis\Redis 运行&#xff1a; redis-server.exe redis.windows.confRedis集群的安装 1.构建集群节点目录 创建一个redis-cluster目录用于存放…

计算机网络 - 路由器查表过程模拟 C++(2024)

1.题目描述 参考计算机网络教材 140 页 4.3 节内容&#xff0c;编程模拟路由器查找路由表的过程&#xff0c;用&#xff08;目的地址 掩码 下一跳&#xff09; 的 IP 路由表以及目的地址作为输入&#xff0c;为目的地址查找路由表&#xff0c;找出正确的下一跳并输出结果。 1.…

ATA-P系列功率放大器在压电叠堆中的作用是什么

功率放大器在压电叠堆中的作用是为压电叠堆提供足够的电能&#xff0c;使其产生强大的机械振动。以下为您详细介绍一下。 压电叠堆是一种利用压电效应产生振动的器件。通过在叠堆上施加电压&#xff0c;叠堆内部的压电材料会产生机械应变&#xff0c;从而引起叠堆的振动。这种振…