我正在将一个大型Txt文档读入WPF应用程序,以进行一些严重的swap / replacemnt操作。这些文件实际上是3D STP模型,因此它们相当大,但我正在使用它们作为该项目的原始文本。这些文件被读入List,以避免多次打开它们,并使比较更容易。
无论如何,我试图让列表框在添加行时动态滚动,ala一个控制台窗口,这样用户就可以看到发生了什么,因为计算可能需要一些时间,具体取决于文件大小。我还添加了一个进度条,可以在读取总行数时计算出来。
我的进度条和ListBox似乎都没有随着工作的进展而更新。最终输出只是在列表框中完成,进度条同时从0-max开始。
这是我正在做的事情的要点,这很简单:
foreach (string Line in OriginalSTPFile.Lines)
{
string NewLine = EvaluateString(Line); //string the modified to whatever here
pBar.Value++; //increment progressbar
OutputWindow.Items.Add(NewLine); //add line to the ListBox
}
我只想让列表框成为进度条,以便在进度发生变化时实时更新。我尝试使用:
Dispatcher.BeginInvoke(new Action(() => OutputWindow.Items.Add(NewLine));
但得到了相同的结果。我需要更精细的多线程方法吗?我假设第一种方法可以工作,因为我没有产生任何跨线程异常。
答案 0 :(得分:1)
答案 1 :(得分:1)
Dispatcher.BeginInvoke指示调用Dispatcher线程上的方法。但是,这基本上就像一条帖子消息,因为在主线程被锁定做工作时不会发生。在主线程再次可用之前,即使您更改了值,它也不会直观地更新UI。
您需要在后台线程中执行工作。
但要更新UI,您必须在UI的主线程上执行此操作。这是WPF的限制。这就是你被导向Dispatcher的原因。我猜有人认为你的工作已经在后台主题上了。
要创建线程,请使用Thread.Start
传递委托来执行。如果您使用匿名委托或lambda,您可以引用堆栈上的变量,但要注意它们将一直存在,直到委托退出。这就是您不能在匿名委托中使用引用变量的原因。
Backgroundworker是一种特殊类型的后台线程。它可以自动化工作线程的一些期望(通知完成和更新进度),但如果没有它,您可以获得相同的结果。
要在线程进程期间更新UI,您需要该线程能够访问主UI线程。您可以通过向调度程序传递,从匿名委托外部引用调度程序或者包含调度程序的对象来执行此操作。您总是可以从任何线程上的任何对象读取值,因此在另一个线程上通过UIElement访问调度程序就可以了。
要更新用户界面,您需要使用需要执行工作的代理人来调用Dispatcher.BeginInvoke
。
这是整个方案的伪代码
class TestProgress
{
ProgressBar _ProgressBar;
void DoWork()
{
var worker = (Action)(() =>
{
int progress = 0;
// do stuff, delta is change in progress
progress += delta;
_ProgressBar.Dispatcher.BeginInvoke((Action)(() =>
{
_ProgressBar.Value = progress;
}));
});
Thread.Start(worker);
}
}