1

I'm writing an app, that performs very long requests at background. After each request I need to send result to main form.

So, here is a code:

Form1.cs

private async void StartButton_Click(object sender, EventArgs e)
{
    await Logic.GenerateStackAsync(stackSettings, delegate(FullOrder transaction)
          {
              lastOrderId.Text = transaction.OrderId;
          }
    );

    MessageBox.Show("Completed!");
}

Logic.cs:

    public static bool GenerateStack(StackSettings stackSettings, Action<FullOrder> onOrderCreated = null)
    {
         for(var i = 0; i < 10; i++) 
         {
              // long, long request, replaced with:
              System.Threading.Thread.Sleep(10000);
              if (onOrderCreated != null)
              {
                  onOrderCreated.Invoke(order);
                  // tried to change it with onOrderCreated(order), no results.
              }
          }

          return true;
    }

    public static Task<bool> GenerateStackAsync(StackSettings stackSettings, Action<FullOrder> onOrderCreated)
    {
        return TaskEx.Run(() => GenerateStack(stackSettings, onOrderCreated));
    }

It throws an exception: "Control 'lastOrderId' accessed from a thread other than the thread it was created on.", which can be fixed by adding CheckForIllegalCrossThreadCalls = false;, but I think that this is a bad experience. How make it right? Thank you in advance.

P.S. Sorry for bad English.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
kopaty4
  • 2,236
  • 4
  • 26
  • 39

3 Answers3

1

First, do not expose (fake-)asynchronous wrappers for your synchronous methods.

Next, if you want to report progress updates, then use the progress update classes provided in .NET for that purpose.

public static bool GenerateStack(StackSettings stackSettings, IProgress<FullOrder> progress = null)
{
     for(var i = 0; i < 10; i++) 
     {
          // long, long request, replaced with:
          System.Threading.Thread.Sleep(10000);
          if (progress != null)
          {
              progress.Report(order);
          }
      }

      return true;
}

Then, you can call it as such:

private async void StartButton_Click(object sender, EventArgs e)
{
  var progress = new Progress<FullOrder>(transaction =>
  {
      lastOrderId.Text = transaction.OrderId;
  });
  await Task.Run(() => Logic.GenerateStack(stackSettings, progress));

  MessageBox.Show("Completed!");
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
-1

I would say that you need to use Control.Invoke to solve that problem:

See http://msdn.microsoft.com/library/system.windows.forms.control.invoke(v=vs.110).aspx

Dirk Huber
  • 902
  • 6
  • 12
  • That helps, thank you. Replaced `lastOrderId.Text = transaction.OrderId;` with `Invoke((MethodInvoker) delegate { lastOrderId.Text = transaction.OrderId; });` and now it works. – kopaty4 Jan 25 '14 at 18:41
-1

when you use async\await u actually starting new thread you do you stuff there and you want the result to show in the main thread the UIThread thats why you need to use the Control.Invoke

NoahBon
  • 1
  • 2
  • 4