1

I am trying to create some UserControl(s) using another thread, and I am using code like this:

    private void btnDemo_Click(object sender, RoutedEventArgs e)
    {
      Task tsk = Task.Factory.StartNew(() =>
      {
        for (int i = 0; i < 3; i++)
        {
          MyControl sprite = new MyControl();
          pnlTest.Children.Add(sprite);
        }
      });
    }

But I am getting this exception in the UserControl constructor:

The calling thread must be STA, because many UI components require this.

I am not sure that I am using the right approach to do this. Please, Can you help me with this.

thanks.

Robin-Hood
  • 345
  • 3
  • 11

2 Answers2

2

The creating of the controls can be done on any Thread but Adding them to the GUI needs to be synchronized to the main Thread.

In this case, just 3 controls, forget about Tasks and just do it directly, single-threaded.

H H
  • 263,252
  • 30
  • 330
  • 514
  • actually there are 100 controls thus I am looking for multi threading – Robin-Hood Feb 03 '11 at 10:25
  • @Robin: tough luck, the important stuff has to be done on the main thread anyway. You could use a Task to prepare a list of Controls, make sure they don't need a Handle. – H H Feb 03 '11 at 10:28
  • Thanks, I’ve tried this, but, the exception is still there! Actually calling the constructor from other thread is what causes the exception, not adding the controls to the window. – Robin-Hood Feb 03 '11 at 10:42
  • @Robin: OK, I thought the ctor would be safe for simple controls but I'm not 100% sure. Anyway, the savings would be small. Threads/Tasks are only useful for splitting of large(ish) non-GUI work. If your controls load data, you may be able to save something there. – H H Feb 03 '11 at 11:21
  • 1
    "The creating of the controls can be done on any Thread but Adding them to the GUI needs to be synchronized to the main Thread" - that's wrong. WPF controls are DispatcherObjects and hence have thread affinity. They can only be accessed in the thread in which they were created. As a consequence, all elements in a WPF object tree have to be created in the same thread, i.e. the UI thread. – Clemens Apr 01 '19 at 19:21
  • @Clemens - you are correct, this statement is more about WinForms than WPF. As I have learned, even non-Controls like the JpegDecoder require a Dispatcher and therefore a HWND. It's a mess. – H H Apr 01 '19 at 22:21
  • Not a HWND, just an STA thread. WPF also works in a console application, without any window. – Clemens Apr 02 '19 at 05:52
  • Every Dispatcher object needs a Dispatcher needs a Window. Don't use WPF classes in a Server app. – H H Apr 02 '19 at 16:15
1

You can dispatch the operation of adding controls to the Children collection to the UI thread using Dispatcher:

private void btnDemo_Click(object sender, RoutedEventArgs e)
{
  Task tsk = Task.Factory.StartNew(() =>
  {
    for (int i = 0; i < 3; i++)
    {
      Dispatcher.BeginInvoke(new Action(() => {
         MyControl sprite = new MyControl();
         pnlTest.Children.Add(sprite);
      }));
    }
  });
}

By calling BeginInvoke on Dispatcher you basically adding the operation to the queue to execute on the UI thread.

Pavlo Glazkov
  • 20,498
  • 3
  • 58
  • 71
  • Correct, but now all the work (99.9%) is Dispatched to the main thread. The Task only creates overhead and complication. – H H Feb 03 '11 at 11:58
  • 1
    @Henk Holterman - Maybe the code provided is simplified and there are some other things going on in the loop. If no, still this ways the UI will remain responsive while the list of controls is being populate, which I believe was the whole purpose of the multi-threading here. – Pavlo Glazkov Feb 03 '11 at 12:16
  • How can the UI thread respond before it finishes handling all of the items in its queue? – Robert Rossney Feb 03 '11 at 19:47
  • 1
    It remains responsive because between handling these items it also handles input events (because they have higher or the same dispatcher priority). – Pavlo Glazkov Feb 03 '11 at 19:59