我正在做的大概是:
label.Text = "Please Wait..."
try
{
SomewhatLongRunningOperation();
}
catch(Exception e)
{
label.Text = "Error: " + e.Message;
return;
}
label.Text = "Success!";
操作前标签文本未设置为“请稍等...”。
我使用另一个线程解决了这个问题,但代码变得复杂了,我希望简化代码。
label.Text = "Please Wait..."
try
{
SomewhatLongRunningOperation();
}
catch(Exception e)
{
label.Text = "Error: " + e.Message;
return;
}
label.Text = "Success!";
操作前标签文本未设置为“请稍等...”。
我使用另一个线程解决了这个问题,但代码变得复杂了,我希望简化代码。
一开始我想知道为什么楼主还没有将其中一个回答标记为答案,但在尝试后仍然不起作用后,我深入挖掘发现这个问题比我最初想象的要复杂得多。
通过阅读类似问题的内容可以更好地理解:为什么控件无法在中途更新/刷新。
最后,为了记录,我成功更新标签的方法如下:
private void SetStatus(string status)
{
lblStatus.Text = status;
lblStatus.Invalidate();
lblStatus.Update();
lblStatus.Refresh();
Application.DoEvents();
}
尽管据我所知,这远非一种优雅且正确的处理方法。这是一种可能有效或无效取决于线程忙碌程度的黑客方式。
Refresh()
相当于先调用 Invalidate()
再调用 Update()
,因此在这里您实际上进行了两次操作。 - Chris Morgan.Refresh()
也相当无用,见下面我的回答。Application.DoEvents()
是你唯一需要的命令,虽然它只应该在非常简单的程序中使用。对于其他情况,最好使用真正的线程(例如使用BackgroundWorker)。 - Tobias Knauss在设置标签之后,调用 Application.DoEvents()
,但是应该在单独的线程中完成所有工作,以便用户可以关闭窗口。
Application.DoEvents
可能会引入有趣的问题(线程也会出现),例如如果用户点击一个按钮再次触发操作时,它仍在运行会发生什么? - Fredrik Mörk调用label.Invalidate
然后调用label.Update()
- 通常情况下,更新只会在退出当前函数后发生,但是调用Update会强制在代码的特定位置进行更新。
来自MSDN:
Invalidate方法控制什么被绘制或重新绘制。Update方法控制何时进行绘制或重新绘制。如果您使用Invalidate和Update方法而不是调用Refresh,则重新绘制取决于您使用哪个Invalidate重载。Update方法只是强制立即绘制控件,但是当您调用Update方法时,Invalidate方法控制要绘制的内容。
label.Invalidate()
(没有参数)后跟label.Update()
等效于label.Refresh()
。 - Chris Morgan如果您只需要更新一些控件,使用.update()就足够了。
btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
btnMyButton.Update(); // after I added this, it turned green quickly
我刚刚遇到了同样的问题,并找到了一些有趣的信息。在这里,我想要发表我的看法并添加进来。
首先,正如其他人已经提到的,长时间运行的操作应该由一个线程来处理,它可以是一个后台工作者、一个显式线程、一个线程池中的线程或者(自 .Net 4.0) 一个任务: Stackoverflow 570537: update-label-while-processing-in-windows-forms, 这样UI就会保持响应。
但是对于短时间的任务,实际上没有必要使用多线程,虽然当然也不会有什么坏处。
我创建了一个包含一个按钮和一个标签的winform来分析这个问题:
System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
label1->Text = "Start 1";
label1->Update();
System::Threading::Thread::Sleep(5000); // do other work
}
label1->Text ...
处设置断点,将应用程序窗口放在VS窗口旁边,并将鼠标光标放在VS窗口上。)this->Update();
也没有帮助更新整个表单。
- 添加Application::DoEvents();
给UI一个机会来更新/重绘。无论如何,您必须确保用户不能按下按钮或在UI上执行其他不允许的操作!!因此:尽量避免使用DoEvents()!最好使用线程(我认为在.Net中非常简单)。但是(@Jagd,Apr 2 '10 at 19:25),您可以省略.refresh()
和.invalidate()
。<>玩笑<结论:所以你只需要阻止用户使用鼠标;-) </joke>
using System.Windows.Forms; // u need this to include.
MethodInvoker updateIt = delegate
{
this.label1.Text = "Started...";
};
this.label1.BeginInvoke(updateIt);
看看它是否可行。
更新UI后,启动一个任务来执行长时间运行的操作:
label.Text = "Please Wait...";
Task<string> task = Task<string>.Factory.StartNew(() =>
{
try
{
SomewhatLongRunningOperation();
return "Success!";
}
catch (Exception e)
{
return "Error: " + e.Message;
}
});
Task UITask = task.ContinueWith((ret) =>
{
label.Text = ret.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
这适用于.NET 3.5及更高版本。
我想我有答案了,从上面和一些实验中提炼出来。
progressBar.Value = progressBar.Maximum - 1;
progressBar.Maximum = progressBar.Value;
progressBar.Value
设置为 progressBar.Maximum
,这种方式行不通,因为您不能将进度条的值设置为高于最大值,所以我首先将 progressBar.Value
设置为 progressBar.Maximum -
1,然后将 progressBar.Maximum
设置为等于 progressBar.Value
。有人说杀死猫有多种方法。有时候我想杀死比尔·盖茨或者现在是谁就好了 :o)。Invalidate()
、Refresh()
、Update()
或者做任何关于进度条或其面板容器或父窗体的操作。很有诱惑力想要“修复”它并强制更新UI,但最好的解决方案是在后台线程上执行此操作,而不是占用UI线程,以便它仍然可以响应事件。
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate(VS.80).aspx