使用C#和WinForms創(chuàng)建動態(tài)圖表的兩種方法
前言
這次我們將學(xué)習(xí)如何使用 C# 和 WinForms 創(chuàng)建動態(tài)圖表。我們將使用 Chart 控件來創(chuàng)建圖表,并使用多線程技術(shù)實(shí)現(xiàn)動態(tài)更新圖表數(shù)據(jù)的效果。
方法一:在項(xiàng)目啟動時(shí)實(shí)例化圖表
在 DoWork 方法中,我們使用 Random 類生成隨機(jī)數(shù)作為 Y 值,并使用 Series 對象的 Points 集合來添加數(shù)據(jù)點(diǎn)。如果數(shù)據(jù)點(diǎn)數(shù)量超過 20 條,我們將移除最舊的數(shù)據(jù)點(diǎn),并更新所有數(shù)據(jù)點(diǎn)的 X 坐標(biāo)值,以確保僅顯示最新的 20 條數(shù)據(jù)。
- 初始化圖表
在窗體的構(gòu)造函數(shù)中,創(chuàng)建一個(gè) Chart 控件,并將其 Dock 屬性設(shè)置為 Fill,以使其占據(jù)整個(gè)窗體的空間。然后創(chuàng)建一個(gè) ChartArea,并將其添加到 Chart 控件上。接著創(chuàng)建兩個(gè) Series(系列),并將它們都添加到 Chart 控件上。最后將 Chart 控件的 BorderSkin.SkinStyle 屬性設(shè)置為 None,以隱藏外部邊框。
// 初始化圖表
chart1 = new Chart
{
Parent = this,
Dock = DockStyle.Fill
};
// 創(chuàng)建一個(gè)新的圖表區(qū)域
ChartArea chartArea1 = new ChartArea();
chart1.ChartAreas.Add(chartArea1);
// 設(shè)置圖表區(qū)域的邊框顏色為透明
chartArea1.BorderColor = Color.FromArgb(255, 0, 0);
// 創(chuàng)建倆個(gè)折線圖系列
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;
- 點(diǎn)擊按鈕后增加數(shù)據(jù)點(diǎn)
當(dāng)用戶點(diǎn)擊“開始”按鈕時(shí),會觸發(fā) Button1_Click 事件處理程序。在該事件處理程序中,創(chuàng)建一個(gè)新線程并調(diào)用 DoWork 方法,在該方法中不斷地添加新數(shù)據(jù)點(diǎn)。每次添加新數(shù)據(jù)點(diǎn)時(shí),先生成一個(gè)隨機(jī)數(shù)作為 Y 坐標(biāo)值,然后調(diào)用 BeginInvoke 方法,將更新 UI 元素的代碼封裝在一個(gè) Action 委托中,并將該委托傳遞給 BeginInvoke 方法。這樣可以確保 UI 元素的更新操作是異步執(zhí)行的,從而避免阻塞 UI 界面。
private void Button1_Click(object sender, EventArgs e)
{
// 創(chuàng)建新線程
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
- 更新數(shù)據(jù)點(diǎn)的 X 坐標(biāo)值
如果數(shù)據(jù)點(diǎn)的數(shù)量超過了20個(gè),就需要移除最舊的數(shù)據(jù)點(diǎn)。移除操作后,需要更新剩余數(shù)據(jù)點(diǎn)的 X 坐標(biāo)值,確保僅顯示最新的20個(gè)數(shù)據(jù)點(diǎn)。這是通過循環(huán)遍歷所有數(shù)據(jù)點(diǎn)并更新其 X 坐標(biāo)值實(shí)現(xiàn)的。
- 刷新圖表
每次添加新數(shù)據(jù)點(diǎn)后,需要調(diào)用 chart1.DataBind() 方法刷新圖表,并將 xValue 的值自增1,以便下一次添加新數(shù)據(jù)點(diǎn)時(shí)能夠使用正確的 X 坐標(biāo)值。
private void DoWork()
{
while (true)
{
// 檢查窗體是否已經(jīng)關(guān)閉
if (IsDisposed || Disposing)
{
return;
}
// 每次點(diǎn)擊按鈕時(shí),增加X值和隨機(jī)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)
{
// 如果數(shù)據(jù)點(diǎn)數(shù)量超過100條,移除最舊的數(shù)據(jù)點(diǎn)
series1.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點(diǎn)的X坐標(biāo)值,確保僅顯示最新的100條數(shù)據(jù)
for (int i = 0; i < series1.Points.Count; i++)
{
series1.Points[i].XValue = i + 1;
}
}
if (series2.Points.Count > 20)
{
// 如果數(shù)據(jù)點(diǎn)數(shù)量超過100條,移除最舊的數(shù)據(jù)點(diǎn)
series2.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點(diǎn)的X坐標(biāo)值,確保僅顯示最新的100條數(shù)據(jù)
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
};
// 創(chuàng)建一個(gè)新的圖表區(qū)域
ChartArea chartArea1 = new ChartArea();
chart1.ChartAreas.Add(chartArea1);
// 設(shè)置圖表區(qū)域的邊框顏色為透明
chartArea1.BorderColor = Color.FromArgb(255, 0, 0);
// 創(chuàng)建倆個(gè)折線圖系列
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窗體添加一個(gè)名為Button1的按鈕 一個(gè)名為Button1的方法
private void Button1_Click(object sender, EventArgs e)
{
// 創(chuàng)建新線程
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
private void DoWork()
{
//循環(huán)無限添加點(diǎn) 形成折線圖
while (true)
{
// 檢查窗體是否已經(jīng)關(guān)閉
if (IsDisposed || Disposing)
{
return;
}
// 每次點(diǎn)擊按鈕時(shí),增加X值和隨機(jī)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)
{
// 如果數(shù)據(jù)點(diǎn)數(shù)量超過100條,移除最舊的數(shù)據(jù)點(diǎn)
series1.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點(diǎn)的X坐標(biāo)值,確保僅顯示最新的100條數(shù)據(jù)
for (int i = 0; i < series1.Points.Count; i++)
{
series1.Points[i].XValue = i + 1;
}
}
if (series2.Points.Count > 20)
{
// 如果數(shù)據(jù)點(diǎn)數(shù)量超過100條,移除最舊的數(shù)據(jù)點(diǎn)
series2.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點(diǎn)的X坐標(biāo)值,確保僅顯示最新的100條數(shù)據(jù)
for (int i = 0; i < series2.Points.Count; i++)
{
series2.Points[i].XValue = i + 1;
}
}
// 更新X值
xValue++;
// 刷新圖表
chart1.DataBind();
}));
Thread.Sleep(500);
}
}
}
}
運(yùn)行結(jié)果

在 Form1 構(gòu)造函數(shù)中,我們初始化了圖表控件,創(chuàng)建了一個(gè)新的圖表區(qū)域和兩個(gè)折線圖系列,并將這些元素添加到圖表控件上。
在運(yùn)行程序后,當(dāng)用戶點(diǎn)擊按鈕時(shí),圖表控件將開始顯示動態(tài)的折線圖,隨著時(shí)間的推移,新的數(shù)據(jù)點(diǎn)將加入圖表中,并移除最舊的數(shù)據(jù)點(diǎn)。
這是直接在頁面打開的時(shí)候進(jìn)行初始化 一個(gè)Chart圖表 ,和倆條Series折線圖Line
方法二:拖動控件的方法創(chuàng)建圖表
工具箱的位置

步驟一
輸入Chart 將 Chart拖動到From1上

步驟二
打開Chart的屬性,找到屬性Series,打開集合添加成員類型(series就是一個(gè)圖表),找到左側(cè)的圖表屬性ChartType,選擇Line。這句話等效于如下代碼。
// 創(chuàng)建倆個(gè)折線圖系列
series1 = new Series
{
ChartType = SeriesChartType.Line
};
series2 = new Series
{
ChartType = SeriesChartType.Line
};

步驟三
完成上面步驟最后應(yīng)該是如下圖

全部代碼
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)
{
// 創(chuàng)建新線程
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
private void DoWork()
{
//將倆條折線取出來
Series series1 = chart2.Series[0];
Series series2 = chart2.Series[1];
while (true)
{
// 檢查窗體是否已經(jīng)關(guān)閉
if (IsDisposed || Disposing)
{
return;
}
// 每次點(diǎn)擊按鈕時(shí),增加X值和隨機(jī)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)
{
// 如果數(shù)據(jù)點(diǎn)數(shù)量超過100條,移除最舊的數(shù)據(jù)點(diǎn)
series1.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點(diǎn)的X坐標(biāo)值,確保僅顯示最新的100條數(shù)據(jù)
for (int i = 0; i < series1.Points.Count; i++)
{
series1.Points[i].XValue = i + 1;
}
}
if (series2.Points.Count > 20)
{
// 如果數(shù)據(jù)點(diǎn)數(shù)量超過100條,移除最舊的數(shù)據(jù)點(diǎn)
series2.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點(diǎn)的X坐標(biāo)值,確保僅顯示最新的100條數(shù)據(jù)
for (int i = 0; i < series2.Points.Count; i++)
{
series2.Points[i].XValue = i + 1;
}
}
// 更新X值
xValue++;
// 刷新圖表
chart2.DataBind();
}));
Thread.Sleep(500);
}
}
}
}
運(yùn)行結(jié)果

擴(kuò)展
以下是一些常見的 chart2.Series 屬性和方法的使用示例:
//添加一個(gè)新系列:
Series newSeries = new Series();
chart2.Series.Add(newSeries);
//刪除指定索引位置的系列:
chart2.Series.RemoveAt(index);
//獲取指定索引位置的系列:
Series series = chart2.Series[index];
//遍歷所有系列:
foreach (Series series in chart2.Series)
{
// 對每個(gè)系列執(zhí)行操作
}
//設(shè)置系列的屬性,如圖表類型和系列名稱:
series.ChartType = SeriesChartType.Line;
series.Name = "Series Name";
//清空所有系列:
chart2.Series.Clear();
注意事項(xiàng)
必須用線程來啟動這個(gè)方法,異步更新 UI 元素,防止動態(tài)卡死。
以上就是使用C#和WinForms創(chuàng)建動態(tài)圖表的兩種方法的詳細(xì)內(nèi)容,更多關(guān)于C# WinForms創(chuàng)建動態(tài)圖表的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.
本篇文章,小編為大家介紹關(guān)于C# 5.0 CallerMemberName CallerFilePath CallerLineNumber 在.NET4中的使用介紹方法,有需要的朋友可以參考一下2013-04-04
使用C#?11的靜態(tài)接口方法改進(jìn)?面向約定?的設(shè)計(jì)方法
我們知道接口是針對契約的定義,但是一直以來它只能定義一組“實(shí)例”的契約,而不能定義類型的契約,因?yàn)槎x在接口中的方法只能是實(shí)例方,這篇文章主要介紹了使用C#?11的靜態(tài)接口方法改進(jìn)面向約定?的設(shè)計(jì),需要的朋友可以參考下2022-12-12
C++聯(lián)合體轉(zhuǎn)換成C#結(jié)構(gòu)的實(shí)現(xiàn)方法
這篇文章主要介紹了C++聯(lián)合體轉(zhuǎn)換成C#結(jié)構(gòu)的實(shí)現(xiàn)方法,需要的朋友可以參考下2014-08-08
C# 靜態(tài)構(gòu)造函數(shù)使用總結(jié)
今天花了一些時(shí)間把靜態(tài)構(gòu)造函數(shù)的用法總結(jié)了一下,希望高手們指點(diǎn)。謝謝2013-03-03

