Передача объекта из рабочего в основной поток - PullRequest
4 голосов
/ 22 июля 2011

У меня есть приложение WPF, которое выполняет внешние программы для обработки мультимедийных файлов, и чтобы графический интерфейс не зависал при обработке мультимедийных файлов, я выполняю этот процесс в отдельном потоке с помощью backgroundworker.

        private void BackgroundWorkerExecProcess(Process process)
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.WorkerReportsProgress = false;
        worker.DoWork += DoWork;
        worker.RunWorkerCompleted += WorkerCompleted;
        worker.RunWorkerAsync(process);
    }
    void DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        Process process = e.Argument as Process;
        process.Start();
        string stderr = process.StandardError.ReadToEnd();
        //I want to display stderr on main thread
        process.WaitForExit();
    }
    void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //some code to update gui telling user that process has finished
    }

Итак, если что-то напечатано в stderr, я могу увидеть это в отладчике, но если я попытаюсь что-то сделать со строкой stderr, например, если у меня есть текстовое поле с именем "_tbLog" и сделал

_tbLog.Text+=stderr;    

Я получаю сообщение от компилятора об их нахождении в отдельных потоках. Есть ли способ передать объект из рабочего потока в основной поток?

Ответы [ 4 ]

8 голосов
/ 22 июля 2011

В DoWork установите e.Result для вашего объекта.В WorkerCompleted вы можете вернуть этот объект ... он снова будет e.Result типа объекта.Просто приведите это к объекту, которым это было.WorkerCompleted должен быть в правильном потоке.

Вот один из моих:

private void workerUpdateBuildHistory_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    UpdateStatusModel model = (UpdateStatusModel)e.Argument;
    BuildService buildService = new BuildService(model.TFSUrl);
    e.Result = buildService.UpdateBuildHistoryList(model);
}

private void workerUpdateBuildHistory_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    BuildHistoryListModel model = (BuildHistoryListModel)e.Result;
    if (model != null)
    {
        listViewPastBuilds.Items.Clear();
        foreach (var item in model.Builds)
        {
            listViewPastBuilds.Items.Add(item);
        }
    }
}
3 голосов
/ 22 июля 2011

Используйте ваш обработчик событий WorkerCompleted для внесения изменений в пользовательский интерфейс, он работает в нужном потоке. Все, что вам нужно сделать, это передать строку в обработчик событий. Именно для этого и был разработан DoWorkEventArgs.Result. Вы получите его в обработчике событий из e.Result. Таким образом:

   void DoWork(object sender, DoWorkEventArgs e)
   {
      //...
      e.Result = stderr;
   }

   void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
   {
      if (e.Error != null) DisplayError(e.Error);
      else _tbLog.Text += (string)e.Result;
   }
2 голосов
/ 22 июля 2011

Сначала вам нужно поместить любой объект результата (в этом примере, список строк) в свойство DoWorkEventArgs.Result, а затем извлечь этот объект через свойство RunWorkerCompletedArgs.Result

Затем подключить событиеобработчик события RunWorkedCompleted фонового работника и позволяющий ему передать обратно любой объект, который вы хотите в свойстве RunWorkerCompletedEventArgs.Result.

Пример:

void DoWork(object sender, DoWorkEventArgs arg)
{
   List<string> results = new List<string>();
   results.Add("one");
   results.Add("two");
   results.Add("three");
   arg.Results = results;
}

void WorkComplete(object sender, runWorkerCompelteEventArgs arg)
{
   //Get our result back as a list of strings
   List<string> results = (List<string>)arg.Result;
   PrintResults(results);
}

Примечание. Я не проверял этот код, ноЯ считаю, что это должно скомпилироваться.http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.result.aspx http://msdn.microsoft.com/en-us/library/system.componentmodel.doworkeventargs.aspx

0 голосов
/ 22 июля 2011

вы также можете использовать диспетчер, как @Zembi упоминает:

  this.Dispatcher.Invoke( new Action( () => {
    _tbLog.Text+=stderr; 
  } ) );

вы также можете использовать TPL , чтобы убедиться, что все работает в нужном потоке

-edit-

Вот хорошая статья о различных способах обновления пользовательского интерфейса, включая использование TPL

...