【问题标题】:Try to write from the background worker to a textbox in the GUI尝试从后台工作人员写入 GUI 中的文本框
【发布时间】:2023-12-23 12:05:02
【问题描述】:

我现在已经花了 2 个多小时试图解决这个问题,如果有人可以帮助我,那就太棒了 .. :)

基本上,我尝试的是一个使用 LINQ 和 web 服务查询数据库并检索信息的应用程序。

我从结果中提取了一些信息并将它们写入 .CSV 文件。

一切都完美无缺,除了日志记录。

因为我不希望我的 UI 卡死,所以我实现了一个后台工作者:

我将我的记录器文本框交给后台工作人员,我从我的静态 Web 服务方法中写入进度。

using TextBoxAsAlias = System.Windows.Controls.TextBox;

这是我第一次写到记录器文本框,它工作得非常完美。

private void btnExecute_Click_1(object sender, RoutedEventArgs e)
        {
            // Register background worker
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;

            // Flush the content of the logger textbox
            txtboxLogger.Clear();
            txtboxLogger.AppendText("#########################" + Environment.NewLine);
            txtboxLogger.AppendText("# Logger - " + DateTime.Now.ToString("T") + "#" + 
        txtboxLogger.AppendText("#########################" + Environment.NewLine + Environment.NewLine);


        worker.RunWorkerAsync(new Object[] {txtboxLogger });

            }

这是我遇到问题的地方:

如您所见,我再次尝试在 DoWork 方法中记录一些文本。 问题是,当 worker_DoWork 完成时,整个文本才会被记录。 因此,我等待了 5 分钟,记录器文本框中没有任何反应,一旦完成,所有内容都会立即写入。

void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            Object[] parameter = (Object[])e.Argument;
            TextBoxAsAlias textboxLogger = (TextBoxAsAlias)parameter[0];

            textboxLogger.Dispatcher.InvokeAsync((Action)(() =>
            {
                txtboxLogger.AppendText(DateTime.Now.ToString("T") + " - Start processing ... " + Environment.NewLine);

                if (isAutoSelection)
                {
                    // Execute Webservice with auto selection
                    RandomDoWorkMethod(null, context, credential, dateStart, textboxLogger);
                }

                else
                {
                    // Read în the Numbers + hand it over to the webservice for further computing
                    RandomDoWorkMethod(ReadInputFile(), context, credential, dateStart, textboxLogger);
                }

            }));
        }

有谁知道我如何在后台 Worker 期间立即写入日志文件,而不仅仅是在最后?

我在其他方法中进一步使用了以下代码,但结果还是一样,因为它们都在worker_DoWork线程中。

textboxLogger.Dispatcher.InvokeAsync((Action)(() =>
            {
))};

提前感谢您的帮助。

问候

乔治

【问题讨论】:

  • 在诊断中使用 Trace

标签: c# wpf multithreading background worker


【解决方案1】:

使用 UI 线程的 SynchronizationContext。

全局变量:

private System.Threading.SynchronizationContext _uiSyncContext;

在构造函数中:

this._uiSyncContext = System.Threading.SynchronizationContext.Current;

在 worker_DoWork 方法中使用 UI 元素:

this._uiSyncContext.Post(
  delegate(object state)
  {
    txtboxLogger.AppendText(state as string);
  },
  "Your Text"
);

您也可以使用 Send(同步)方法代替 Post(异步)。

【讨论】:

    【解决方案2】:

    使用这样的调度程序会将所有工作推回 UI,您应该只使用调度程序不时刷新文本,切勿使用调度程序推送RandomDoWorkMethod

    还有consider using binding(另见BackgroundWorkerProgressChanged 的注释)。

    【讨论】:

      【解决方案3】:

      这听起来像是使用 ProgressChanged 事件的标准场景。

      BackgroundWorker 线程不能直接与 UI 线程通信,但它确实公开了一个可以与之通信的事件。以下是您的设置方式:

      在“注册后台工作人员”部分,添加以下行:

      worker.WorkerReportsProgress = true;
      worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
      

      您需要在类中的某处定义处理程序,使其看起来像这样:

      private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
      {
          if (e.ProgressPercentage == 0)
          {
              string message = DateTime.Now.ToString("T") + " - Start processing ... ";
              txtboxLogger.AppendText(message + Environment.NewLine);                 
          }
      }
      

      请注意,我们假设进度为 0 意味着我们刚刚开始我们的流程。该值需要在 DoWork 事件处理程序中发送。所以你的 DoWork 方法现在看起来像这样:

      private void worker_DoWork(object sender, DoWorkEventArgs e)
      {
          BackgroundWorker worker = sender as BackgroundWorker;
          worker.ReportProgress(0);
      
          if (isAutoSelection)
          {
              // Execute Webservice with auto selection
              RandomDoWorkMethod(null, context, credential, dateStart, textboxLogger);
          }
          else
          {
              // Read în the Numbers + hand it over to the webservice for further computing
              RandomDoWorkMethod(ReadInputFile(), context, credential, dateStart, textboxLogger);
          }
      }
      

      【讨论】: