BackgroundWorker - отчет о прогрессе с помощью «подзадач» - PullRequest
1 голос
/ 15 апреля 2020

Приложение WinForms с пользовательским элементом управления LabelProgressBar, которое может отображать как ход выполнения, так и некоторый описательный текст и / или процент завершения. Это делается путем вызова LabelProgressBar.statusInProgress(string message, int percentageCompletion).

. Это можно использовать следующим образом:

private void import_begin(System.Object sender, System.ComponentModel.DoWorkEventArgs args)
{
    // first unpack the arguments
    System.Object[] arguments = (System.Object[])args.Argument;
    System.String filename = (System.String)arguments[0];
    System.String why = (System.String)arguments[1];

     // tasks:
     // 1. read excel file and apply changes to model
     // 2. gather changes and format them as XML
     // 3. send request to server
     // 4. commit/rollback changes

      // grab the worker thread so we can report percentage progress
      System.ComponentModel.BackgroundWorker worker = (System.ComponentModel.BackgroundWorker)sender;

     // now do the work
     #region Task1
     Controller.Excel excel = new Controller.Excel(filename);
     try
     {
          // the progress of this needs to be tracked
          overall_result = excel.import_all(out modified_nodes);
     }
     catch (InvalidDataExcetpion invDataEx)
     {
         // deal with it
     }
     #endregion
     worker.ReportProgress(25);

     // complete remaining tasks...
}

Обработчик события для работника, сообщающего о своем прогрессе, выглядит следующим образом:

private void import_progress(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
    Debug.WriteLine("Import percentage completion: " + e.ProgressPercentage);
    labelProgressBar1.statusInProgress("Import", e.ProgressPercentage);
}

Короче говоря, метод import_begin разбит на несколько «задач». Они разбиты на «подзадачи». Возьмем пример метода import_all:

public Command_Result import_all(out System.Collections.Generic.List<Model.Data_Node> nodes)
        {
            Command_Result overall_result = Command_Result.OK;
            Command_Result this_result;
            nodes = new System.Collections.Generic.List<Model.Data_Node>(excel.Workbook.Worksheets.Count);
            Model.Data_Node destination;

            // the intent is to report the progress of this particular subtask on the basis of how many worksheets have been processed in this for loop
            foreach (OfficeOpenXml.ExcelWorksheet worksheet in excel.Workbook.Worksheets)
            {
                this_result = import_sheet(worksheet.Name, out destination);
                nodes.Add(destination);
                if (this_result > overall_result)
                {
                    overall_result = this_result;
                }
            }

            return overall_result;
        }

Намерение состоит в том, чтобы этот отчет «подзадачи» отображал прогресс на основе того, сколько листов было обработано в l oop. Вычисление процента для этого является тривиальной задачей, но мне не ясно, как об этом можно сообщить в методе import_begin. Когда эта «подзадача» завершена, общее выполнение задачи (из POV метода import_begin) должно составлять 25%. Аналогично для других задач. Как этого достичь?

1 Ответ

1 голос
/ 15 апреля 2020

import_begin на самом деле не нужно получать обновление, он может просто вызывать подзадачи, , в то время как также пропускает BackgroundWorker, поэтому подзадачи несут ответственность за непосредственное сообщение о своем прогрессе. Если «загрязнение» подзадач с помощью BackgroundWorker недопустимо, то создайте делегат для вызова BackgroundWorker, чтобы ваши подзадачи затем вызывали делегат.

private void mainTask(object sender, DoWorkEventArgs e)
{
    var worker = (BackgroundWorker)sender;
    var report = new Action<int>(i => worker.ReportProgress(i)); //the delegate
    smallTask1Clean(report); //this one pass the delegate
    smallTask2(worker); //this one directly call background worker
    worker.ReportProgress(100);
}

void smallTask1Clean(Action<int> a)
{
    for (int i = 0; i < 20; i++)
    {
        Thread.Sleep(500);
        a(i);
    }
}

void smallTask2(BackgroundWorker w)
{
    for (int i = 0; i < 5; i++)
    {
        Thread.Sleep(1000);
        w.ReportProgress(i*80/5+20);
    }
}

Вы также можете изолировать подзадачи от необходимости знать свою роль в более крупных задачах, в этом случае делегат должен принимать две переменные: текущий внутренний ход подзадач и общее количество элементов, которые он должен обработать.

private void mainTask(object sender, DoWorkEventArgs e)
{
    var worker = (BackgroundWorker)sender;
    var preTaskProgress = 0;
    var currentTaskTotalPercentage = 0;
    var smarterDelegate = new Action<int, int>((current, total) =>
     {
         worker.ReportProgress(preTaskProgress + (current *currentTaskTotalPercentage/total));
     });

    currentTaskTotalPercentage = 30; //the following task will in total progressed the main task for 30%
    smallTaskClean(smarterDelegate);
    preTaskProgress = currentTaskTotalPercentage; //upate the main the progress before starting the next task
    currentTaskTotalPercentage = 70; //the following task will in total progressed the main task for 70%
    smallTaskClean(smarterDelegate);

    worker.ReportProgress(100);
}

void smallTaskClean(Action<int,int> a)
{
    for (int i = 0; i < 5; i++)
    {
        Thread.Sleep(1500);
        a(i,5);
    }
}
...