告别界面卡死!用多线程+委托实现流畅的 WinForm 进度条

246 阅读4分钟

前言

在Windows桌面应用开发中,进度条(ProgressBar)是用户界面中不可或缺的元素,尤其在执行耗时操作(如文件读写、数据处理、网络请求)时,它能有效提升用户体验,让用户感知程序的运行状态。然而,由于WinForm的UI线程是单线程模型,直接在主线程中执行耗时任务会导致界面"卡死"。因此,必须采用多线程技术,将耗时操作放在后台线程执行,同时通过委托机制安全地更新UI。

本文将详细介绍如何在C# WinForm中实现一个异步更新的进度条,涵盖窗体设计、委托定义、线程控制与UI安全更新等核心知识点。

效果图

4eec81fa3102efe0bd2c276df12055e4_412334-20211009190014130-155258611.png

e44e600f1daf325e59c1a9cddcc4133a_412334-20211009190028932-1028251721.png

窗体设计

首先,创建一个新的WinForm窗体,并根据需求进行界面布局。本例中,我们隐藏窗体的标题栏以获得更简洁的外观,并从工具箱中拖拽一个ProgressBar控件(命名为pgbWrite)和一个Label控件(命名为lblProcess)到窗体上,用于显示进度和状态信息。

4f5143c4db8c7ce1d8bf7068a83d7499_412334-20211009184828569-245592554.png

后台代码实现

定义委托

为了实现跨线程更新UI,需要定义委托。委托是一种类型安全的函数指针,可以在不同线程间传递方法。

delegate void AsynUpdateUI(int step);

该委托用于异步更新UI,参数step表示进度条每次增加的步长。

窗体加载事件

在窗体的Load事件中,初始化进度条,并启动后台线程执行耗时任务。

private void FormClient_Load(object sender, EventArgs e)
{
    int taskCount = 100;
    this.pgbWrite.Maximum = taskCount;
    this.pgbWrite.Value = 0;

    DataWrite dataWrite = new DataWrite();//实例化一个写入数据的类
    dataWrite.UpdateUIDelegate += UpdataUIStatus;//绑定更新任务状态的委托
    dataWrite.TaskCallBack += Accomplish;//绑定完成任务要调用的委托

    Thread thread = new Thread(new ParameterizedThreadStart(dataWrite.Write));
    thread.IsBackground = true;
    thread.Start(taskCount);
}

代码说明

  • 设置进度条的最大值为100,表示任务总步数。

  • 创建DataWrite类的实例,并将UpdataUIStatusAccomplish方法绑定到其委托。

  • 创建并启动一个后台线程,将taskCount作为参数传递给Write方法。

DataWrite 类实现

该类封装了耗时任务的逻辑,通过委托通知主线程更新UI。

internal class DataWrite
{
    public delegate void UpdateUI(int step);//声明一个更新主线程的委托
    public UpdateUI UpdateUIDelegate;

    public delegate void AccomplishTask();//声明一个在完成任务时通知主线程的委托
    public AccomplishTask TaskCallBack;

    public void Write(object lineCount)
    {
        for (int i = 0; i < (int)lineCount; i++)
        {
            //编写要完成事情的代码,目前先用等待代替
            Thread.Sleep(50); //模拟耗时操作

            //写入一条数据,调用更新主线程ui状态的委托
            UpdateUIDelegate(1);
        }
        //任务完成时通知主线程作出相应的处理
        TaskCallBack();
        //将更新包信息写入到客户端文件配置中
        Thread.Sleep(1000);
        Application.Exit();
    }
}

更新前端UI的方法

由于DataWrite类在后台线程中运行,不能直接访问UI控件。必须使用InvokeBeginInvoke方法将更新操作封送回UI线程。

private void UpdataUIStatus(int step)
{
    if (InvokeRequired)
    {
        this.Invoke(new AsynUpdateUI(delegate (int s)
        {
            this.pgbWrite.Value += s;
            this.lblProcess.Text = "检测到最新程序,正在更新请稍候("+this.pgbWrite.Value.ToString() + "%)...";
        }), step);
    }
    else
    {
        this.pgbWrite.Value += step;
        this.lblProcess.Text = "检测到最新程序,正在更新请稍候(" + this.pgbWrite.Value.ToString() + "%)...";
    }
}

InvokeRequired属性用于判断当前代码是否在UI线程执行。如果是,则直接更新控件;否则,通过Invoke方法在UI线程上执行更新逻辑。

任务完成后的处理

当后台任务完成后,通过TaskCallBack委托调用Accomplish方法,更新UI状态。

private void Accomplish()
{
    if (InvokeRequired)
    {
        this.Invoke(new AsynUpdateUI(delegate (int s)
        {
            lblProcess.Text = "更新完成,即将启动客户端...";
        }), 0);
    }
    else {
        lblProcess.Text = "更新完成,即将启动客户端...";
    }
}

总结

本文通过一个完整的示例,展示了如何在C# WinForm中实现异步进度条。

核心要点包括:

  • 使用Thread类创建后台线程执行耗时任务;

  • 通过自定义委托在后台线程与UI线程之间通信;

  • 利用InvokeInvokeRequired确保跨线程UI更新的安全性;

  • 通过ProgressBar控件直观地反馈任务进度。

该方案结构清晰,易于扩展,可广泛应用于软件更新、文件处理、数据导入导出等场景。掌握这一技术,将大大提升开发专业级桌面应用的能力。

关键词

C#、WinForm、进度条、ProgressBar、多线程、委托、Invoke、异步更新、UI线程、后台线程

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:haishu

出处:cnblogs.com/lihaishu/p/15387254.html

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!