C#进度条实现
写在开头
记得本科大二时,和老师一起做项目,那时候刚接触编程没多久,技术很烂,当时有个功能是把shapfile文件中的属性表导出至本地Excel(*xls)格式,就想着能不能做个进度条,提高一下用户体验,于是乎,就兴奋的往窗口上拖个进度条(这个最擅长),然后各种捯饬,后来终于实现了,但是发现只有当表的记录较少时,进度条才能实时更新,否则记录很多时,进度条一直不更新,直至导出完成后进度条直接满血,当时很惆怅,百度了、谷歌了,得知这叫“UI阻塞”必须另开线程才能解决,这下我更惆怅了,那时的我“闻线程色变”,这种“黑技术”我做不到啊!!!
概述
- 实现效果,点击按钮弹出进度条窗口,并开始执行任务,在进度条及文本标签中更新任务进度,关闭进度窗口时可终止任务
- 实现技术,多线程(主线程更新UI,子线程处理耗时任务),委托
代码实现
- 定义委托,限定进度回调函数接口
namespace Progress
{
public delegate void SetProgressValueEventHandler(int pos);//设置进度值的委托 定义参数列表
}
- MainFrm类:主界面,用于启动任务子线程,并开启进度窗口;
namespace Progress
{
public partial class MainFrm : Form
{
public event SetProgressValueEventHandler SetProgressValueEvent;//定义事件用于回调
public Thread ProcessThread //定义子线程用于处理耗时任务
{
get;
set;
}
public MainFrm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ProcessThread = new Thread(new ThreadStart(HandleThread));//子线程初始化
ProgressFrm pFrm = new ProgressFrm(this);
ProcessThread.Start();//开启线程
pFrm.Show();
}
private void HandleThread()//线程处理函数,实现耗时任务
{
Run run = new Run();
run.RunProgress(150, SetProgressValueEvent);
}
}
}
- Run类:耗时任务实现类,实现耗时任务的逻辑代码;
class Run
{
public void RunProgress(int range, SetProgressValueEventHandler setProgressBar)
{
try
{
for (int i = 0; i < range; i++)
{
Thread.Sleep(100);
//int p = 10 / (i - 10);//此处为了验证程序错误响应处理,当i=10时触发异常
setProgressBar(i * 100 / range);
}
setProgressBar(100);//耗时任务结束,进度条达到100;
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.Message);
setProgressBar(101);//处理任务失败,向进度窗口传递消息传递值大于100或小于0的数
}
}
}
- ProgressFrm类:进度控制窗口,用于显示进度。
namespace Progress
{
public partial class ProgressFrm : Form
{
private MainFrm mFrm;
public ProgressFrm(MainFrm mFrm)
{
InitializeComponent();
this.mFrm = mFrm;
//绑定事件处理函数
mFrm.SetProgressValueEvent += new SetProgressValueEventHandler(OnProgressValueChanged);
}
private void OnProgressValueChanged(int pos)//事件处理函数,设置进度条和进度值
{
SetProgressBarValueInvoke(pos);
SetProgressLabelValueInvoke(pos);
}
private void SetProgressBarValueInvoke(int pos)//跨线程调用
{
if (progressBar.InvokeRequired)
{
SetProgressValueEventHandler setProgress = new SetProgressValueEventHandler(SetProgressBarValue);
progressBar.Invoke(setProgress, new object[] { pos });
}
else
{
SetProgressBarValue(pos);
}
}
private void SetProgressBarValue(int pos)
{
//this.progressBar.Value = pos;
if (pos <= progressBar.Maximum && pos >= 0)
{
this.progressBar.Value = pos;
}
}
private void SetProgressLabelValueInvoke(int pos)//跨线程调用
{
if (label.InvokeRequired)
{
SetProgressValueEventHandler setProgressLabel = new SetProgressValueEventHandler(SetProgressLabelValue);
progressBar.Invoke(setProgressLabel, new object[] { pos });
}
else
{
SetProgressBarLabelvalue(pos);
}
}
private void SetProgressLabelValue(int pos)
{
if (pos <= progressBar.Maximum && pos >=0)
{
if (pos == progressBar.Maximum)
{
MessageBox.Show("处理成功···", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.Close();
}
else
{
this.label.Text = pos.ToString() + "%";
}
}
else
{
MessageBox.Show("处理失败···", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.Close();
}
}
private void ProgressFrm_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
mFrm.SetProgressValueEvent -= new SetProgressValueEventHandler(OnProgressValueChanged);
mFrm.ProcessThread.Abort();
}
catch (System.Exception ex)
{
}
}
}
}
总结
现在读研,项目需要C#不常用了,但我想说虽然C#的语法比C++要容易很多(这竟然成为很多人对C#口诛笔伐的缘由),之所以容易而是微软把太多东西封装了,语法虽易但其中确蕴含了更深刻的思想。
封装意味着抽象,意味着更多需要理解的哲学思想。——室友(姜哥)
对于我来说的便是委托/事件对C++函数指针的封装,研究了很久才算理解。
**能力有限请批评指正**