如何使用WinForms进度栏?


101

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

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

但我不想那样做。


4
将委托对象传递给方法。
汉斯·帕桑

Answers:


112

我建议您看看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.
}

5
很好的例子,但是您的代码中有一个小错误。您需要将backgroundWorker.WorkerReportsProgress设置为true。检查我的编辑
Mana

1
@mana假定是BackgroundWorker通过设计器添加的,并在那里进行了配置。但是可以,需要将其配置WorkerReportsProgresstrue
彼得·里奇

啊,我的天哪,不知道您可以在设计器中进行设置
Mana

但是,您想知道其他事情,您的示例仅计为99 backgroundWorker.ReportProgress((j * 100)/ 100000);。如何获得100%计数
法力值

1
@mana如果要显示进度100%,请在RunWorkerCompleted事件处理程序中显示(如果您的DoWork处理程序不显示)。–
彼得·里奇

77

从.NET 4.5开始,您可以结合使用asyncawaitProgress来向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

任务和Progress在这里有更详细的说明:


2
这应该是选定的答案,IMO。好答案!
JohnOpincar '17

几乎是最好的答案,除了它使用Task.Run而不是普通的异步函数
KansaiRobot

@KansaiRobot你的意思是反对await DoWorkAsync(progress);?这非常有意,因为这不会导致额外的线程运行。只有在等待一个I / O操作时才DoWorkAsync调用自己awaitbutton1_Click功能,该功能才能继续。在此期间,主UI线程被阻止。如果DoWorkAsync不是真正的异步,而是很多同步语句,您将一无所获。
Wolfzoon '18年

System.Windows.Controls.ProgressBar不包含包含字段“ Step”。应该从示例中删除它;尤其是因为它还是没有被使用。
罗伯特·陶西格

2
@RobertTausig确实Step只有WinForms的进度栏中可用,这里不需要,但是它存在于问题的示例代码(标记为winforms)中,因此可能会保留。
quasoft

4

嘿,关于点网珍珠有一个有用的教程: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

1

还有就是Task存在的,它是unnesscery使用BackgroundWorkerTask更简单。例如:

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

不要对多个问题发表相同的答案。发布一个好的答案,然后投票/标记以将其他问题重复作为一个重复。如果问题不是重复的,请定制对问题的答案
马丁·彼得斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.