如何使用 WinForms 进度条?

Posted

技术标签:

【中文标题】如何使用 WinForms 进度条?【英文标题】:How to use WinForms progress bar? 【发布时间】:2012-08-21 01:12:22 【问题描述】:

我想显示在外部库中执行的计算进度。

例如,如果我有一些计算方法,并且我想在我的 Form 类中将其用于 100000 个值,我可以编写:

public partial class Form1 : Form

    public Form1()
    
        InitializeComponent();
                

    private void Caluculate(int i)
    
        double pow = Math.Pow(i, i);
    

    private void button1_Click(object sender, EventArgs e)
    
        progressBar1.Maximum = 100000;
        progressBar1.Step = 1;

        for(int j = 0; j < 100000; j++)
        
            Caluculate(j);
            progressBar1.PerformStep();
        
    

我应该在每次计算后执行一步。但是,如果我在外部方法中执行所有 100000 次计算怎么办。如果我不想让这个方法依赖于进度条,我应该什么时候“执行步骤”?例如,我可以写

public partial class Form1 : Form

    public Form1()
    
        InitializeComponent();
    

    private void CaluculateAll(System.Windows.Forms.ProgressBar progressBar)
    
        progressBar.Maximum = 100000;
        progressBar.Step = 1;

        for(int j = 0; j < 100000; j++)
        
            double pow = Math.Pow(j, j); //Calculation
            progressBar.PerformStep();
        
    

    private void button1_Click(object sender, EventArgs e)
    
        CaluculateAll(progressBar1);
    

但我不想那样做。

【问题讨论】:

将委托对象传递给方法。 【参考方案1】:

Task存在,使用BackgroundWorker不方便,Task更简单。例如:

ProgressDialog.cs:

   public partial class ProgressDialog : Form
    
        public System.Windows.Forms.ProgressBar Progressbar  get  return this.progressBar1;  

        public ProgressDialog()
        
            InitializeComponent();
        

        public void RunAsync(Action action)
        
            Task.Run(action);
        
    

完成!然后你可以在任何地方重用 ProgressDialog:

var progressDialog = new ProgressDialog();
progressDialog.Progressbar.Value = 0;
progressDialog.Progressbar.Maximum = 100;

progressDialog.RunAsync(() =>

    for (int i = 0; i < 100; i++)
    
        Thread.Sleep(1000)
        this.progressDialog.Progressbar.BeginInvoke((MethodInvoker)(() => 
            this.progressDialog.Progressbar.Value += 1;
        ));
    
);

progressDialog.ShowDialog();

【讨论】:

请don't post identical answers to multiple questions。发布一个好的答案,然后投票/标记以关闭其他问题作为重复问题。如果问题不是重复的,调整您对该问题的回答 在这段代码中,我将长时间运行的任务放在哪里? @SteveW in RunAsync【参考方案2】:

嘿,有一个关于点网珍珠的有用教程:http://www.dotnetperls.com/progressbar

按照 Peter 的说法,您需要使用一些线程,否则程序会挂起,有点达不到目的。

使用 ProgressBar 和 BackgroundWorker 的示例:C#

using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1

    public partial class Form1 : Form
    
        public Form1()
        
            InitializeComponent();
        

        private void Form1_Load(object sender, System.EventArgs e)
        
            // Start the BackgroundWorker.
            backgroundWorker1.RunWorkerAsync();
        

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        
            for (int i = 1; i <= 100; i++)
            
                // Wait 100 milliseconds.
                Thread.Sleep(100);
                // Report progress.
                backgroundWorker1.ReportProgress(i);
            
        

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        
            // Change the value of the ProgressBar to the BackgroundWorker progress.
            progressBar1.Value = e.ProgressPercentage;
            // Set the text.
            this.Text = e.ProgressPercentage.ToString();
        
    
 //closing here

【讨论】:

【参考方案3】:

从 .NET 4.5 开始,您可以结合使用 async and awaitProgress 向 UI 线程发送更新:

private void Calculate(int i)

    double pow = Math.Pow(i, i);


public void DoWork(IProgress<int> progress)

    // This method is executed in the context of
    // another thread (different than the main UI thread),
    // so use only thread-safe code
    for (int j = 0; j < 100000; j++)
    
        Calculate(j);

        // Use progress to notify UI thread that progress has
        // changed
        if (progress != null)
            progress.Report((j + 1) * 100 / 100000);
    


private async void button1_Click(object sender, EventArgs e)

    progressBar1.Maximum = 100;
    progressBar1.Step = 1;

    var progress = new Progress<int>(v =>
    
        // This lambda is executed in context of UI thread,
        // so it can safely update form controls
        progressBar1.Value = v;
    );

    // Run operation in another thread
    await Task.Run(() => DoWork(progress));

    // TODO: Do something after all calculations

任务目前是实现BackgroundWorker 所做工作的首选方式。

Tasks 和 Progress 在这里有更详细的解释:

Async in 4.5: Enabling Progress and Cancellation in Async APIs Reporting Progress from Async Tasks 斯蒂芬·克利里 Task parallel library replacement for BackgroundWorker?

【讨论】:

这应该是选择的答案,IMO。很好的答案! 几乎是最好的答案,除了它使用的是 Task.Run 而不是普通的异步函数 @KansaiRobot 你的意思是相对于await DoWorkAsync(progress);?这是非常有目的的,因为这不会导致额外的线程运行。仅当 DoWorkAsync 将其称为自己的 await 时,例如等待 I/O 操作,button1_Click 函数是否会继续。主 UI 线程在此期间被阻塞。如果DoWorkAsync 不是真正的异步,而只是很多同步语句,那么您将一无所获。 System.Windows.Controls.ProgressBar 不包含“步骤”字段。它应该从示例中删除;特别是因为它无论如何都没有使用。 @RobertTausig 确实Step 仅在 WinForms 的进度条中可用,此处不需要,但它存在于问题的示例代码(标记为 winforms)中,所以它可能会保留.【参考方案4】:

我建议你看看BackgroundWorker。如果你的 WinForm 中有这么大的循环,它会阻塞,你的应用看起来就像挂了一样。

查看BackgroundWorker.ReportProgress(),了解如何将进度报告回 UI 线程。

例如:

private void Calculate(int i)

    double pow = Math.Pow(i, i);


private void button1_Click(object sender, EventArgs e)

    progressBar1.Maximum = 100;
    progressBar1.Step = 1;
    progressBar1.Value = 0;
    backgroundWorker.RunWorkerAsync();


private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)

    var backgroundWorker = sender as BackgroundWorker;
    for (int j = 0; j < 100000; j++)
    
        Calculate(j);
        backgroundWorker.ReportProgress((j * 100) / 100000);
    


private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)

    progressBar1.Value = e.ProgressPercentage;


private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

    // TODO: do something with final calculation.

【讨论】:

很好的例子,但你的代码中有一个小错误。您需要将 backgroundWorker.WorkerReportsProgress 设置为 true。检查我的编辑 @mana 假设BackgroundWorker 是通过设计器添加并在那里配置的。但是是的,需要将其配置为将WorkerReportsProgress 设置为true 啊,我的错,不知道可以在设计器中设置 想知道别的东西,你的例子只算 99 backgroundWorker.ReportProgress((j * 100) / 100000);如何获得 100% 计数 @mana 如果您想显示 100% 的进度,请在 RunWorkerCompleted 事件处理程序中执行,如果您的 DoWork 处理程序没有执行此操作..

以上是关于如何使用 WinForms 进度条?的主要内容,如果未能解决你的问题,请参考以下文章

如何跟踪从 SQL Server 下载文件的进度?

如何在我的 winforms 应用程序中设置我的 datagrid 滚动条的位置?

vc6.0里MFC进度条如何使用

Delphi:如何使用进度条显示 CreateProcess 的进度?

长按如何使进度条变化Android

如何使用 GWT 实现进度条?