事件不按顺序发生
Events not happening in order
我正在用 C# 创建一个 WinForms 应用程序。当我单击一个按钮时,应该会发生一定的事件流:
- Click Button
- Show label1
- Show label2
- Call function to parse a string the user entered before (this can take awhile depending on the string)
- Show listBox1 and progressBar1
- backgroundWorker1.RunWorkerAsync
- backgroundWorker1_DoWork() does something x number of times and reports progress each time
- backgroundWorker1_ProgressChanged() updates progressBar1 and adds an item to listBox1
- backgroundWorker1_RunWorkCompleted() shows a message box saying "DONE"
但事实并非如此。当我跟踪代码并查看表单时,它有几个问题。
- label1 和 label2 直到解析完成后才真正出现。
- progressBar1 仅在调用 ProgressChanged 时才会更新。其他时候它会等到打印 "DONE" 之后并立即更新。
- 每次调用 progressChange() 时,listBox1 上的垂直滚动条变小,因此我可以看出正在添加项目,但项目的文本直到打印 "DONE" 后才会出现。
我刚开始使用 backgroundWorker,所以我可能只是不明白它应该如何运作。但是延迟显示标签我根本不明白。当我跟踪代码时没有错误,并且行似乎以正确的顺序执行。
有没有人知道可能导致这些问题的原因?我将不胜感激任何帮助或建议。我宁愿不 post 我的代码,只是因为有很多,但如果有人需要它来更好地理解,请 lmk。
编辑:这是代码。
private void button1_Click(object sender, EventArgs e){
label1.Show();
label2.Show();
String errMsg = parseString();
if (errMsg == ""){
listBox1.Items.Clear();
listBox1.Show();
progressBar1.Maximum = 100;
progressBar1.Step = 1;
progressBar1.Value = 0;
progressBar1.Show();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.ReportProgress(1, "Updating Devices");
for (int i = 0; i < 100; i++)
{
//todo: do stuff
//update progress
backgroundWorker1.ReportProgress(i, "Device:" + i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
listBox1.Items.Add(e.UserState);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("DONE");
}
感谢@HansPassant 和@mjwills 的评论。他们让我走上了正确的道路,并使这个解决方案成为可能。
最后我决定做两个后台worker来解决label1和label2解析完才出现的问题。我使用第一个进行解析,第二个进行 "do stuff" 部分。在代码中,您会看到我必须使用 Invoke 来编辑标签,因为该部分现在存在于不同的线程中。
我还意识到调用 ProgressChanged 之前的 "do stuff" 不是即时的。我一直在分段开发并且尚未实现该代码,但我知道完成这些操作至少需要 3 秒(部分原因是涉及 ping)。所以现在我在该循环中调用了一个 Sleep(3000) 来模拟它的实际行为。这解决了由耗尽所有内存引起的奇怪的 progressbar1 和 listbox1 行为。
代码结果如下:
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
String errMsg = parseString();
if (errMsg == "")
{
if (listBox1.InvokeRequired)
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Clear();
listBox1.Show();
}));
}
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new MethodInvoker(delegate
{
progressBar1.Maximum = 100;
progressBar1.Step = 1;
progressBar1.Value = 0;
progressBar1.Show();
}));
}
if (backgroundWorker2.IsBusy != true)
{
backgroundWorker2.RunWorkerAsync();
}
}
else
{
MessageBox.Show(errMsg);
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker2.ReportProgress(1, "Updating Devices");
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(3000);
//do stuff
backgroundWorker2.ReportProgress(i, "Device:" + i);
}
}
private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new MethodInvoker(delegate
{
progressBar1.Value = e.ProgressPercentage;
}));
}
if (listBox1.InvokeRequired)
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(e.UserState);
}));
}
}
private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("DONE");
}
我正在用 C# 创建一个 WinForms 应用程序。当我单击一个按钮时,应该会发生一定的事件流:
- Click Button
- Show label1
- Show label2
- Call function to parse a string the user entered before (this can take awhile depending on the string)
- Show listBox1 and progressBar1
- backgroundWorker1.RunWorkerAsync
- backgroundWorker1_DoWork() does something x number of times and reports progress each time
- backgroundWorker1_ProgressChanged() updates progressBar1 and adds an item to listBox1
- backgroundWorker1_RunWorkCompleted() shows a message box saying "DONE"
但事实并非如此。当我跟踪代码并查看表单时,它有几个问题。
- label1 和 label2 直到解析完成后才真正出现。
- progressBar1 仅在调用 ProgressChanged 时才会更新。其他时候它会等到打印 "DONE" 之后并立即更新。
- 每次调用 progressChange() 时,listBox1 上的垂直滚动条变小,因此我可以看出正在添加项目,但项目的文本直到打印 "DONE" 后才会出现。
我刚开始使用 backgroundWorker,所以我可能只是不明白它应该如何运作。但是延迟显示标签我根本不明白。当我跟踪代码时没有错误,并且行似乎以正确的顺序执行。
有没有人知道可能导致这些问题的原因?我将不胜感激任何帮助或建议。我宁愿不 post 我的代码,只是因为有很多,但如果有人需要它来更好地理解,请 lmk。
编辑:这是代码。
private void button1_Click(object sender, EventArgs e){
label1.Show();
label2.Show();
String errMsg = parseString();
if (errMsg == ""){
listBox1.Items.Clear();
listBox1.Show();
progressBar1.Maximum = 100;
progressBar1.Step = 1;
progressBar1.Value = 0;
progressBar1.Show();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.ReportProgress(1, "Updating Devices");
for (int i = 0; i < 100; i++)
{
//todo: do stuff
//update progress
backgroundWorker1.ReportProgress(i, "Device:" + i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
listBox1.Items.Add(e.UserState);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("DONE");
}
感谢@HansPassant 和@mjwills 的评论。他们让我走上了正确的道路,并使这个解决方案成为可能。
最后我决定做两个后台worker来解决label1和label2解析完才出现的问题。我使用第一个进行解析,第二个进行 "do stuff" 部分。在代码中,您会看到我必须使用 Invoke 来编辑标签,因为该部分现在存在于不同的线程中。
我还意识到调用 ProgressChanged 之前的 "do stuff" 不是即时的。我一直在分段开发并且尚未实现该代码,但我知道完成这些操作至少需要 3 秒(部分原因是涉及 ping)。所以现在我在该循环中调用了一个 Sleep(3000) 来模拟它的实际行为。这解决了由耗尽所有内存引起的奇怪的 progressbar1 和 listbox1 行为。
代码结果如下:
private void button1_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
String errMsg = parseString();
if (errMsg == "")
{
if (listBox1.InvokeRequired)
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Clear();
listBox1.Show();
}));
}
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new MethodInvoker(delegate
{
progressBar1.Maximum = 100;
progressBar1.Step = 1;
progressBar1.Value = 0;
progressBar1.Show();
}));
}
if (backgroundWorker2.IsBusy != true)
{
backgroundWorker2.RunWorkerAsync();
}
}
else
{
MessageBox.Show(errMsg);
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker2.ReportProgress(1, "Updating Devices");
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(3000);
//do stuff
backgroundWorker2.ReportProgress(i, "Device:" + i);
}
}
private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new MethodInvoker(delegate
{
progressBar1.Value = e.ProgressPercentage;
}));
}
if (listBox1.InvokeRequired)
{
listBox1.Invoke(new MethodInvoker(delegate
{
listBox1.Items.Add(e.UserState);
}));
}
}
private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("DONE");
}