C# 进度条是否仅在工作完成后显示?
我试图在表单上显示一个进度条,但由于某些原因,表单在流程结束之前实际上不可见,并且在流程结束时关闭,或者换句话说,表单只打开一瞬间。如何使表单在流程开始时显示 注意:我的代码可能不是100%正确,出于保密原因,我只是想让它不同于我自己的代码C# 进度条是否仅在工作完成后显示?,c#,winforms,progress-bar,C#,Winforms,Progress Bar,我试图在表单上显示一个进度条,但由于某些原因,表单在流程结束之前实际上不可见,并且在流程结束时关闭,或者换句话说,表单只打开一瞬间。如何使表单在流程开始时显示 注意:我的代码可能不是100%正确,出于保密原因,我只是想让它不同于我自己的代码 public void SpawnPizzaProgressBarForm(object sender, EventArgs e) { FormPizzaProgressBar Form = new FormPizzaProgressBar();
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
}
}
这看起来像是一个紧密的循环-您需要使用BackgroundWorker将工作放在后台线程上,或者通过调用Application.DoEvents处理消息队列(其中包括paint命令)来欺骗并给UI时间进行更新
如果循环太紧或工作太繁重,UI将在很大程度上阻塞直到完成,即使UI更改相互混合。您需要使用线程来更新UI,而BackgroundWorker将处理您的实际流程。在开始之前,请先从Estemable Jon Skeet中签出。之所以会出现这种情况,是因为您在中完成了所有处理。在第一次显示窗体之前调用此函数 在事件处理程序中,您会阻止表单实际显示,因为事件处理程序必须在处理任何其他内容之前完成 您要做的是使用实例来执行您的工作。这要求您执行以下操作: 如果默认值为false,则将设置为true 订阅 在DoWork事件处理程序中,调用以报告进度 订阅,这是更新进度条的地方。这也是调用前面提到的ReportProgrss方法时触发的事件htat 订阅,完成后您将在此处关闭表单 调用以启动异步操作 你有一些问题在这里,你有你的比萨饼类紧密耦合到进度条。这不是个好主意。相反,您应该触发一个事件来指示进度已更改,然后从Pizza实例的事件处理程序调用ProgressChanged事件 我已经删除了Eat方法的代码,并将其封装在表单中,以向您展示如何使用BackgroundWorker类的示例,但理想的解决方案是公开一个事件,以指示比萨饼消费量何时发生变化 还请注意,您应该重写,以便在处理窗体时正确处理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();
}
Winforms基于Windows API,它不会更新GUI元素,除非创建GUI元素的线程已启动。WinForms在线程上调用了ProgressForm_Load方法,它必须在该线程上泵送消息,以便更新GUI元素。在Eat中操作期间,您没有泵送消息队列。因此,如果GUI元素不更新,您将看不到进度条的更改 最简单的解决方案是在Eat方法中定期调用Application.DoEvents。更好的方法是在不同的线程上执行长操作,在该操作期间显示沙漏,并使用GUI更新的调用样式定期通知进度条进行更新。除了创建GUI元素的线程之外,您不能直接从其他线程调用大多数GUI元素的方法
PizzaEatingProgressBar.Value = (Slice.Index / Pizza.Count())*100
如果Slice.Index为整数,则不起作用。您需要将其转换为双精度以获得浮点除法。例如:
PizzaEatingProgressBar.Value = (int)((double)Slice.Index / Pizza.Count) * 100);
或者像这样做:
PizzaEatingProgressBar.Value = (Slice.Index * 100) / Pizza.Count;
这确保除法不能像原始表达式那样截断为0。与大家的建议相反,当您更改Value属性时,ProgressBar会自行绘制。尽管您的窗口仍然处于紧张状态。+1为了发现事件,我没有注意到这一点,只是假设UI线程阻塞会导致绘制问题。这是一个很好的答案,更像是一篇信息帖子。非常感谢。