C# 如何使用 WinForms 进度条?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12126889/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 22:09:45  来源:igfitidea点击:

How to use WinForms progress bar?

c#winformsprogress-bar

提问by Dmytro

I want to show progress of calculations, which are performing in external library.

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

For example if I have some calculate method, and I want to use it for 100000 values in my Form class I can write:

例如,如果我有一些计算方法,并且我想在我的 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();
        }
    }
}

I should perform step after each calculation. But what if I perform all 100000 calculations in external method. When should I "perform step" if I don't want to make this method dependant on progress bar? I can, for example, write

我应该在每次计算后执行步骤。但是如果我在外部方法中执行所有 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);
    }
}

but I don't want to do like that.

但我不想那样做。

采纳答案by Peter Ritchie

I would suggest you have a look at BackgroundWorker. If you have a loop that large in your WinForm it will block and your app will look like it has hanged.

我建议你看看BackgroundWorker。如果您的 WinForm 中有一个如此大的循环,它将被阻塞,您的应用程序看起来就像挂了一样。

Look at BackgroundWorker.ReportProgress()to see how to report progress back to the UI thread.

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

For example:

例如:

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.
}

回答by quasoft

Since .NET 4.5 you can use combination of asyncand awaitwith Progressfor sending updates to UI thread:

从 .NET 4.5 开始,您可以使用asyncawait 的组合与Progress将更新发送到 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
}

Tasks are currently the preferred way to implement what BackgroundWorkerdoes.

任务目前是实现功能的首选方式BackgroundWorker

Tasks and Progressare explained in more detail here:

任务和Progress更详细的解释在这里:

回答by Chong Ching

Hey there's a useful tutorial on Dot Net pearls: http://www.dotnetperls.com/progressbar

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

In agreement with Peter, you need to use some amount of threading or the program will just hang, somewhat defeating the purpose.

与 Peter 一致,您需要使用一些线程,否则程序将挂起,有点违背了目的。

Example that uses ProgressBar and BackgroundWorker: C#

使用 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

回答by TangMonk

There is Taskexists, It is unnesscery using BackgroundWorker, Taskis more simple. for example:

有是Task存在的,使用是不必要的BackgroundWorkerTask更简单。例如:

ProgressDialog.cs:

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);
        }
    }

Done! Then you can reuse ProgressDialog anywhere:

完毕!然后你可以在任何地方重用 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();