一、创作背景
之前的文章中分别不覆盖写入csv文件和进度条,本文稍加修改把之前的功能融入程序中,能实现在for循环保存数据的过程中实现中途即时停止循环写入,且保证当前进度下的数据能完整写入csv文件中保存。
二、UI界面介绍
1、文本输入框有:计数开始值、技术步进值、计数停止值。
2、label1用于程序运行过程中显示文字信息。
3、进度条用于显示进度。
4、点击开始按键程序开始保存数据。
5、点击停止按键程序保存完当前循环中的数据并停止运行。
三、程序功能描述
1、该程序用于实现每隔一秒从计数开始值以计数步进值一直累加到计数停止值,同时显示进度并保存系统当前时间、计数值和计数进度到生成的csv文件中。
四、源程序
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace writedata_progressbar
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
bool flag = false;
private async void buttonStart_Click(object sender, EventArgs e)
{
//在执行异步任务前先把UI界面控件的值赋值给局部自定义变量
double CountStart = Convert.ToDouble(textBoxCountStart.Text);
double CountStep = Convert.ToDouble(textBoxCountStep.Text);
double CountStop = Convert.ToDouble(textBoxCountStop.Text);
//根据起始值、步进和终止值创建一个lists
int num = Convert.ToInt32(Math.Abs(CountStop - CountStart) / CountStep) + 1; //计算lists长度
double[] lists = new double[num]; //创建一个长度为num的lists
for (int i = 0; i < num; i++) //根据起始值、步进和终止值生成对应元素放进lists中对应位置
{
if (CountStop > CountStart)
{
lists[i] = CountStart + i * CountStep;
}
else
{
lists[i] = CountStart - i * CountStep;
}
}
string FileNamePath = @"D:\C# Projects\writedata_progressbar\writedata_progressbar.csv";
progressBar1.Maximum = num; //设置进度条最大值为num
await Task.Run(() => //更新UI界面
{
//采用委托的方式更新UI界面label1显示的字符串
if (label1.InvokeRequired) //label1委托请求
{
while (!label1.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (label1.Disposing || label1.IsDisposed)
return;
}
Invoke(new Action(() => { label1.Text = "开始计数"; }));
}
else
{
label1.Text = "开始计数";
}
//采用委托的方式设置buttonStart.Enabled = false,在此期间不允许再次点击该按键
if (buttonStart.InvokeRequired)
{
while (!buttonStart.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (buttonStart.Disposing || buttonStart.IsDisposed)
return;
}
Invoke(new Action(() => { buttonStart.Enabled = false; }));
}
else
{
buttonStart.Enabled = false;
}
});
await Task.Run(async () =>
{
string data = string.Empty;
for (int i = 0; i < lists.Count(); i++)
{
double bar1 = Math.Round((Convert.ToDouble(i + 1) / num) * 100, 3); //计算当前进度值,并保留3位有效数字
//采用委托的方式更新UI界面label1显示的字符串
if (label1.InvokeRequired) //label1委托请求
{
while (!label1.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (label1.Disposing || label1.IsDisposed)
return;
}
Invoke(new Action(() => { label1.Text = "当前时间:" + DateTime.Now.ToString() + "当前计数值:" + Convert.ToString(lists[i]) + ";计数进度为:" + Convert.ToString(bar1) + "%"; }));
}
else
{
label1.Text = "当前时间:"+DateTime.Now.ToString()+"当前计数值:" + Convert.ToString(lists[i]) + ";计数进度为:" + Convert.ToString(bar1) + "%";
}
//采用委托的方式更新UI界面进度条显示的值
if (progressBar1.InvokeRequired) //progressBar1委托请求
{
while (!progressBar1.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (label1.Disposing || label1.IsDisposed)
return;
}
Invoke(new Action(() => { progressBar1.Value = Convert.ToInt32(i + 1); }));
}
else
{
progressBar1.Value = Convert.ToInt32(i + 1);
}
Thread.Sleep(1000);
data = DateTime.Now.ToString() + ',' + "当前计数值:" + Convert.ToString(lists[i]) + ",计数进度为:" + Convert.ToString(bar1) + "%";
//写入数据
if (!File.Exists(FileNamePath))
{
try
{
FileStream fileStream = new FileStream(FileNamePath, FileMode.Create, FileAccess.Write); //实例化文件流类,并指定为创建模式和写
StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.UTF8); //实例化流写类,WriteLine()将以UTF8的编码方式写入数据
streamWriter.WriteLine(Convert.ToString(data)); //写入数据
streamWriter.Close(); //先关闭流写,写完一定要记得关闭,否则文件会被程序打开占用
fileStream.Close(); //再关闭文件流写,写完一定要记得关闭,否则文件会被程序打开占用
}
catch
{
}
}
else
{
try
{
FileStream fileStream = new FileStream(FileNamePath, FileMode.Append, FileAccess.Write); //实例化文件流类,并指定为追加模式和写
StreamWriter streamWriter = new StreamWriter(fileStream, Encoding.UTF8); //实例化流写类,WriteLine()将以UTF8的编码方式写入数据
streamWriter.WriteLine(Convert.ToString(data));
streamWriter.Close(); //先关闭流写,写完一定要记得关闭,否则文件会被程序打开占用
fileStream.Close(); //再关闭文件流写,写完一定要记得关闭,否则文件会被程序打开占用
}
catch
{
}
}
if (flag == true) //按下停止键则停止运行
{
flag = false;
break;
}
}
});
await Task.Run(() =>
{
//采用委托的方式设置buttonStart.Enabled = true,允许再次点击该按键
if (buttonStart.InvokeRequired)
{
while (!buttonStart.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (buttonStart.Disposing || buttonStart.IsDisposed)
return;
}
Invoke(new Action(() => { buttonStart.Enabled = true; }));
}
else
{
buttonStart.Enabled = true;
}
});
}
private async void buttonpause_Click(object sender, EventArgs e) //按下按键给flag取反
{
await Task.Run(() =>
{
//采用委托的方式设置buttonstop.Enabled = false,在此期间不允许再次点击该按键
if (buttonstop.InvokeRequired)
{
while (!buttonstop.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (buttonstop.Disposing || buttonstop.IsDisposed)
return;
}
Invoke(new Action(() => { buttonstop.Enabled = false; }));
}
else
{
buttonstop.Enabled = false;
}
});
await Task.Run(() =>
{
if (flag == false)
{
flag = true;
}
else
{
flag = false;
}
});
await Task.Run(() =>
{
//采用委托的方式设置buttonstop.Enabled = true,允许再次点击该按键
if (buttonstop.InvokeRequired)
{
while (!buttonstop.IsHandleCreated)
{
//解决窗体关闭时出现“访问已释放句柄“的异常
if (buttonstop.Disposing || buttonstop.IsDisposed)
return;
}
Invoke(new Action(() => { buttonstop.Enabled = true; }));
}
else
{
buttonstop.Enabled = true;
}
});
}
}
}
五、实际效果演示
熟练使用C#中的异步(async)编程实现WinForm程序带进度提示啊保存数据(有开始和停止按键的实现)