进度条仅在工作完成后出现?

时间:2010-07-27 15:06:53

标签: c# winforms progress-bar

我正在尝试在表单上显示进度条,但由于某种原因,表单在进程结束之前实际上不可见,并且在进程结束时关闭(或者换句话说,表格只是暂时开放)。如何才能使表格显示在流程开始时?

注意:我的代码可能不是100%正确,我只是因为保密原因而试图使它与我自己的不同。

public void SpawnPizzaProgressBarForm(object sender, EventArgs e)
{
    FormPizzaProgressBar Form = new FormPizzaProgressBar();
    Form.ShowDialog();
}
...
public void ProgressBarForm_Load(object sender, EventArgs e)
{
    Pizza = new Pizza();
    Pizza.Eat(PizzaEatingProgressBar);
    this.Close();
}
...
public void Eat(ProgressBar PizzaEatingProgressBar)
{
    foreach(var Slice in Pizza)
    {
        Slice.Clear(); //
        PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100
    }
}

5 个答案:

答案 0 :(得分:6)

这是因为您在Load event for the form中执行了所有处理。这在第一次显示表单之前称为

在事件处理程序中,您阻止表单实际显示,因为事件处理程序必须先完成才能处理其他任何内容。

您要做的是使用BackgroundWorker实例来执行您的工作。这要求您执行以下操作:

这里有一些问题,你的Pizza课程与进度条紧密相连。这不是一个好主意。相反,您应该触发一个事件以指示进度已更改,然后从Pizza实例的事件处理程序中调用ProgressChanged事件。

我已经移出了Eat方法的代码并将其封装在表单中,向您展示如何使用BackgroundWorker类的示例,但理想的解决方案是公开事件以指示披萨的数量消费变化。

另请注意,在处理表单时,应覆盖protected Dispose method on the Form class以正确处置BackgroundWorker实例。

以下是示例:

public void SpawnPizzaProgressBarForm(object sender, EventArgs e)
{
    FormPizzaProgressBar Form = new FormPizzaProgressBar();
    Form.ShowDialog();
}

...

BackgroundWorker worker = new BackgroundWorker();

public void ProgressBarForm_Load(object sender, EventArgs e)
{
    // Initialize the background worker.
    worker = new BackgroundWorker();

    // Indicate that the worker supports progress.
    worker.WorkerSupportsProgress = true;

    // Subscribe to the DoWork event.
    worker.DoWork += (s, e) => {
        // Create the pizza instance.
        Pizza = new Pizza();

        // Process the slices.
        foreach (var Slice in Pizza)
        {
            // Clear the slice.
            Slice.Clear();

            // Report the progress.
            worker.ReportProgress(Slice.Index / Pizza.Count() * 100);
        }
    };

    // Subscribe to the ProgressChanged event.
    worker.ProgressChanged = (s, e) => {
        // Update the progress bar.
        PizzaEatingProgressBar.Value = e.ProgressPercentage;
    };

    // Subscribe to the RunWorkerCompleted event.
    worker.RunWorkerCompleted = (s, e) => {
        // Close the dislog.
        this.Close();
    };
}

// Must override to properly dispose of the background worker.
protected override void Dispose(bool disposing)
{
    // Call the base.
    base.Disposing(disposing);

    // Dispose of the background worker if disposing is true.
    if (disposing) worker.Dispose();
}

答案 1 :(得分:3)

Winforms基于Windows API,它不会更新GUI元素,除非创建GUI元素的线程被“抽取”。 WinForms在线程上调用了您的ProgressForm_Load方法,它必须“抽取”消息才能更新GUI元素。您在Eat操作期间没有抽取消息队列。因此,如果GUI元素没有更新,您将看不到进度条的更改。

最简单的解决方案是定期在Application.DoEvents方法中调用Eat。更好的答案是在不同的线程上进行长时间操作,在该操作期间显示沙漏,并使用Invoke样式的GUI更新定期通知进度条进行更新(不能调用大多数GUI元素) '直接来自创建它们的线程以外的线程的方法。

答案 2 :(得分:2)

 PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100

如果Slice.Index为整数,则不起作用。您需要将其转换为double以获得浮点除法。例如:

 PizzaEatingProgressBar.Value = (int)((double)Slice.Index / Pizza.Count) * 100);

或者这样做:

 PizzaEatingProgressBar.Value = (Slice.Index * 100) / Pizza.Count;

这确保除法不能像原始表达式那样截断为0。与每个人的建议相反,当您更改Value属性时,ProgressBar 绘制自己。虽然你的窗户仍然是紧张性精神病。

答案 3 :(得分:1)

这看起来像一个紧凑的循环 - 你需要使用BackgroundWorker将工作放在后台线程上,或者作弊并通过调用Application.DoEvents()给UI更新时间(处理消息队列) ,包括绘画命令)。

如果循环太紧或工作太重,UI将在很大程度上阻塞,直到它完成,即使UI更改混合在一起。

答案 4 :(得分:0)

当BackgroundWorker处理您的实际流程时,您需要使用线程来更新UI。在开始之前,请查看this article(来自精通的Jon Skeet)。