我正在尝试在表单上显示进度条,但由于某种原因,表单在进程结束之前实际上不可见,并且在进程结束时关闭(或者换句话说,表格只是暂时开放)。如何才能使表格显示在流程开始时?
注意:我的代码可能不是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
}
}
答案 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)。