Обновление пользовательского интерфейса из событий с использованием asyc ждут - программирование
Подтвердить что ты не робот

Обновление пользовательского интерфейса из событий с использованием asyc ждут

Я пытаюсь понять, как обновлять пользовательский интерфейс от события при использовании шаблона async/await. Ниже приведен тестовый код, который я использую в приложении WinForm. Я даже не уверен, что это правильный путь. Что необходимо, чтобы позволить модулю pwe_StatusUpdate обновлять пользовательский интерфейс? Здесь выдается ошибка работы поперечной нити.

Спасибо за чтение.

 // calling code
    ProcessWithEvents pwe = new ProcessWithEvents();
    pwe.StatusUpdate += pwe_StatusUpdate;
    await pwe.Run();



 void pwe_StatusUpdate(string updateMsg)
    {
      // Error Here: Cross-thread operation not valid: Control '_listBox_Output' accessed from a thread other than the thread it was created on.
      _listBox_Output.Items.Add(updateMsg);
    }

-

// Class with long running process and event    
public delegate void StatusUpdateHandler(string updateMsg);

 public class ProcessWithEvents
  {
    public event StatusUpdateHandler StatusUpdate;

    public async Task Run()
    {
        await Task.Run(() =>
        {
            for (int i = 0; i < 10; i++)
                {

                    RaiseUpdateEvent(String.Format("Update {0}", i));

                    Thread.Sleep(500);
                }
            });

        }

        private void RaiseUpdateEvent(string msg)
        {
        if (StatusUpdate != null)
            StatusUpdate(msg);
        }
   }

-

4b9b3361

Ответ 1

Шаблон async поддерживает обновления прогресса.

Короче говоря, ваш метод async может принимать IProgress<T>, а ваш код вызова проходит в реализации этого интерфейса (обычно Progress<T>).

public class ProcessWithUpdates
{
  public async Task Run(IProgress<string> progress)
  {
    await Task.Run(() =>
    {
      for (int i = 0; i < 10; i++)
      {
        if (progress != null)
          progress.Report(String.Format("Update {0}", i));
        Thread.Sleep(500);
      }
    });
  }
}

// calling code
ProcessWithUpdates pwp = new ProcessWithUpdates();
await pwp.Run(new Progress<string>(pwp_StatusUpdate));

Ответ 2

Вы должны использовать Invoke метод Control. Он выполняет некоторый код в потоке управления. Также вы можете проверить свойство InvokeRequired, чтобы проверить, нужно ли вам вызывать метод Invoke (он проверяет, является ли вызывающий объект иным, чем тот, на котором был создан элемент управления).

Простой пример:

void SomeAsyncMethod()
{
    // Do some work             

    if (this.InvokeRequired)
    {
        this.Invoke((MethodInvoker)(() =>
            {
                DoUpdateUI();

            }
        ));
    }
    else
    {
        DoUpdateUI();
    }
}

void DoUpdateUI()
{
    // Your UI update code here
}

В некоторых случаях вы должны проверить свойство IsHandleCreated Control перед вызовом метода Invoke. Если IsHandleCreated возвращает false, вам нужно подождать, пока будет создан дескриптор управления

Ответ 3

//Просто объявляйте делегат таким образом

delegate void Add(string msg);

//Затем объявим метод делегата следующим образом:

var add = new Add((msg) => {
   _listBox_Output.Items.Add(msg);
});

//Теперь просто вызовите делегата:

void pwe_StatusUpdate(string updateMsg)
    {

      _listBox_Output.Invoke(add,updateMsg);
    }

Ответ 4

Вот еще один пример

async void DoExport()
{
    var rMsg = "";
    var t = await Task<bool>.Factory.StartNew(() => ExportAsMonthReport(LastMonth.Name, LastYear.Name, out rMsg));

    if (t)
    {
          BeginInvoke((Action)(() =>
          {
               spinnerMain.Visible = false;
               menuItemMonth.Enabled = true;

               MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Information, 200);
          }));
   }
   else
   {
          BeginInvoke((Action)(() =>
          {
               spinnerMain.Visible = false;
               menuItemMonth.Enabled = true;

               MetroMessageBox.Show(this, rMsg, "Export", MessageBoxButtons.OK, MessageBoxIcon.Error, 200);
          }));
    }
}