2

我正在将大型 Txt 文档读入 WPF 应用程序,以进行一些严重的交换/替换操作。这些文件实际上是 3D STP 模型,因此它们相当大,但我将它们作为该项目的原始文本使用。这些文件被读入 List 以避免必须多次打开它们,并使比较更容易。

无论如何,我试图让列表框在添加行时动态滚动,例如控制台窗口,以便用户可以看到正在发生的事情,因为计算可能需要一些时间,具体取决于文件大小。我还添加了一个进度条,以便在读取总行号时进行计数。

不过,我的进度条和 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));

但得到了相同的结果。我需要更精细的多线程方法吗?我认为第一种方法会起作用,因为我也没有生成任何跨线程异常。

4

2 回答 2

1

本文将为您提供所需的所有代码。

带有进度条的后台工作者

它很好地描述了要做什么以及要使用哪些元素。

于 2013-02-13T20:15:55.980 回答
1

Dispatcher.BeginInvoke 发出信号以调用 Dispatcher 线程上的方法。然而,这本质上就像一个帖子消息,因为它不会在主线程被锁定工作时发生。并且在主线程再次可用之前,即使您更改了值,它也不会直观地更新 UI。

您需要在后台线程中执行工作。

但要更新 UI,您必须在 UI 的主线程上执行此操作。这是 WPF 的一个限制。这就是您被定向到 Dispatcher 的原因。我猜有人认为你的工作已经在后台线程上。

要创建一个线程,您可以使用Thread.Start将委托传递给它来执行。如果您使用匿名委托或 lambda,您可以引用堆栈上的变量,但请注意它们将持续存在,直到委托退出。这就是为什么您不能在匿名委托中使用引用变量的原因。

Backgroundworker 是一种特殊类型的后台线程。它自动化了工作线程的一些期望(通知完成,并更新进度),但没有它你也可以获得相同的结果。

要在线程进程中更新 UI,您需要该线程能够访问主 UI 线程。您可以通过向它传递一个调度程序,从匿名委托外部引用一个调度程序,或通过一个包含调度程序的对象来做到这一点。您始终可以从任何线程上的任何对象读取值,因此可以通过 UIElement 在另一个线程上访问调度程序。

要更新 UI,您将调用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);
    }
}
于 2013-02-13T20:46:36.213 回答