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

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

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

步驟二
打開Chart的屬性,找到屬性Series,打開集合添加成員類型(series就是一個圖表),找到左側(cè)的圖表屬性ChartType,選擇Line。這句話等效于如下代碼。
// 創(chuàng)建倆個折線圖系列
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;
}
// 每次點擊按鈕時,增加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)
{
// 如果數(shù)據(jù)點數(shù)量超過100條,移除最舊的數(shù)據(jù)點
series1.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點的X坐標值,確保僅顯示最新的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ù)點數(shù)量超過100條,移除最舊的數(shù)據(jù)點
series2.Points.RemoveAt(0);
// 更新所有數(shù)據(jù)點的X坐標值,確保僅顯示最新的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);
}
}
}
}
運行結(jié)果

擴展
以下是一些常見的 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)
{
// 對每個系列執(zhí)行操作
}
//設(shè)置系列的屬性,如圖表類型和系列名稱:
series.ChartType = SeriesChartType.Line;
series.Name = "Series Name";
//清空所有系列:
chart2.Series.Clear();
注意事項
必須用線程來啟動這個方法,異步更新 UI 元素,防止動態(tài)卡死。
以上就是使用C#和WinForms創(chuàng)建動態(tài)圖表的兩種方法的詳細內(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)接口方法改進?面向約定?的設(shè)計方法
我們知道接口是針對契約的定義,但是一直以來它只能定義一組“實例”的契約,而不能定義類型的契約,因為定義在接口中的方法只能是實例方,這篇文章主要介紹了使用C#?11的靜態(tài)接口方法改進面向約定?的設(shè)計,需要的朋友可以參考下2022-12-12
C++聯(lián)合體轉(zhuǎn)換成C#結(jié)構(gòu)的實現(xiàn)方法
這篇文章主要介紹了C++聯(lián)合體轉(zhuǎn)換成C#結(jié)構(gòu)的實現(xiàn)方法,需要的朋友可以參考下2014-08-08
C# 靜態(tài)構(gòu)造函數(shù)使用總結(jié)
今天花了一些時間把靜態(tài)構(gòu)造函數(shù)的用法總結(jié)了一下,希望高手們指點。謝謝2013-03-03

