我正在尝试在WinForms C#应用程序中创建一个单独的线程,启动一个控制ProgressBar(marquee)的后台工作程序。问题在于,当我尝试将条形图设置为可见时,它什么都不做,我尝试过多种形式的Invoke,但它们似乎没有帮助。
从单独的线程调用以下方法progressBarCycle
。
BackgroundWorker backgroundWorker = new BackgroundWorker();
public void progressBarCycle(int duration)
{
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.RunWorkerAsync(duration);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(0);
DateTime end = DateTime.Now.AddMilliseconds((int)e.Argument);
while (DateTime.Now <= end)
{
System.Threading.Thread.Sleep(1000);
}
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!this.IsHandleCreated)
this.CreateHandle();
statusStrip1.Invoke((MethodInvoker)delegate
{
progressBar1.Visible = false;
});
// if (!this.IsHandleCreated)
// {
// this.CreateHandle();
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false));
// else progressBar1.Visible = false;
// }
// else
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = false));
// else progressBar1.Visible = false;
}
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!this.IsHandleCreated)
this.CreateHandle();
statusStrip1.Invoke((MethodInvoker)delegate
{
progressBar1.Visible = true;
});
// if (!this.IsHandleCreated)
// {
// this.CreateHandle();
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true));
// else progressBar1.Visible = true;
// }
// else
// if (InvokeRequired) this.Invoke((MethodInvoker)(() => progressBar1.Visible = true));
// else progressBar1.Visible = true;
}
我错过了一些明显的东西吗?评论部分是我尝试过的其他内容。
答案 0 :(得分:6)
ProgressChanged
已经在UI线程上引发(通过sync-context);您的ProgressChanged
不需要Invoke
- 它可以直接操作UI(相比之下,DoWork
绝对可以不这样做)。也许真正的问题是你没有在循环中做任何worker.ReportProgress(...)
- 所以它只在开始时发生一次。
以下是一个完整的例子:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using (var worker = new BackgroundWorker {
WorkerReportsProgress = true })
using (var progBar = new ProgressBar {
Visible = false, Step = 1, Maximum = 100,
Dock = DockStyle.Bottom })
using (var btn = new Button { Dock = DockStyle.Top, Text = "Start" })
using (var form = new Form { Controls = { btn, progBar } })
{
worker.ProgressChanged += (s,a) => {
progBar.Visible = true;
progBar.Value = a.ProgressPercentage;
};
worker.RunWorkerCompleted += delegate
{
progBar.Visible = false;
};
worker.DoWork += delegate
{
for (int i = 0; i < 100; i++)
{
worker.ReportProgress(i);
Thread.Sleep(100);
}
};
btn.Click += delegate
{
worker.RunWorkerAsync();
};
Application.Run(form);
}
}
}
答案 1 :(得分:2)
progressBarCycle
。 RunWorkerAsync
会
为你创建新的主题。backgroundWorker_ProgressChanged
中,只需致电
progressBar1.Visible = true;
。不需要Invoke
。progressBar1.Refresh();
。答案 2 :(得分:2)
另一个需要注意的可能是进度条正在您的UI线程上运行。为了显示进度条并重绘自身以显示新的进度数量,UI线程必须自由运行,在主应用程序循环中处理Windows消息。
因此,如果您启动后台工作线程,但是您的UI线程处于忙等待循环中等待它完成,或者关闭并执行大量其他工作,那么它将不会处理Windows消息和您的进度酒吧将“反应迟钝”。您需要释放UI线程,以便仍然发生此更新(即从事件处理程序返回并允许UI继续正常运行)。
这样做的危险在于,如果UI处于活动状态,则用户仍然可以与其进行交互。因此,您必须编写要在后台工作程序处于活动状态时要注意的UI,并正确处理该情况(问题可能包括:允许用户在其已经运行时再次启动后台工作程序,UI尝试在工作时显示信息线程忙于更新它,用户决定加载新文档或在后台工作者忙时退出,等等。对此的两个主要解决方案是将每个UI包裹在一个保护屏蔽中,以阻止在后台工作运行时发起的任何危险(如果你有很多控件以这种方式包装,这可能是很多工作,并且很容易犯错误,让错误漏掉)或让UI“不受保护”,但添加一个IMessageFilter,通过抑制他们传入的Windows消息(WM_KEYPRESS等)来阻止所有“危险”的用户交互(点击和按键)后台处理活跃。