3

I am trying to call a method from BackgroundWorker that adds a user control to ListView:

private void AddItems(List<FileItem> fileItems) {
    System.Threading.Thread.CurrentThread.SetApartmentState(System.Threading.ApartmentState.STA);
    Dispatcher.BeginInvoke(new Action(() => files.ItemsSource = fileItems));
}

The user control files is getting the data from fileItems successfully in its constructor but it's throwing The calling thread must be STA, because many UI components require this. exception. I already tried adding [STAThread] attribute to all parent methods one by one but still it's throwing the exception. Where should I add this attribute?

UPDATE

Please also note that Dispatcher.BeginInvoke(new Action(() => files.Items.Clear())); is executing correctly.

Aishwarya Shiva
  • 3,460
  • 15
  • 58
  • 107
  • 1
    You cannot call SetApartmentState() when the thread is already started. You cannot arbitrarily apply [STAThread], it only works on the Main() entrypoint. Use the proper dispatcher, Application.Current.Dispatcher probably. You are suppose to use BackgroundWorker.ReportProgress or RunWorkerCompleted instead. – Hans Passant Aug 21 '15 at 18:31
  • @HansPassant please see my update. And I tried your way as I mentioned in comment to answer by user2864740. But its again throwing the same exception. – Aishwarya Shiva Aug 21 '15 at 19:39

2 Answers2

1

This Dispatcher refers to one associated with the BGW thread, not the WPF/UI thread. By default a new dispatcher/context will be created if none is associated with the current thread; in context this is entirely useless.

If supplying the dispatcher instance (Dispatcher.CurrentInstance) from the UI thread that starts the BGW then it 'ought to work'. Likewise, as Hans points out, the correct (WPF/UI) dispatcher object should be accessible through the dispatcher associated with the Application.

Additionally an arbitrary object can be supplied in the ReportProgress method; this allows sending information back to the parent. The processing of the UI components can then done in the event handler which is automatically run on the correct WPF/UI thread. (The same holds for 'work complete' processing.)

There is no need to set any STA threading options/attributes for a standard WPF project.

Community
  • 1
  • 1
user2864740
  • 60,010
  • 15
  • 145
  • 220
  • I did like this `worker.ReportProgress(90, fileItems);` and enabled `WorkerReportsProgress=true`. Also, called `files.ItemsSource = fileItems` in the `ProgressChanged` event. But still its not working. Am I doing it correctly? – Aishwarya Shiva Aug 21 '15 at 19:30
  • "Not working" = ? (In any case it should probably be done at worker-complete, if setting all the items, vs. doing an incremental change which represents 'progress'.) – user2864740 Aug 21 '15 at 19:37
  • Apologies for not being specific. But I meant that its still showing the same exception. – Aishwarya Shiva Aug 21 '15 at 19:40
  • That is .. odd. No idea then. One thing to try might be to force materialization of the source if it is a enumerable, eg. `worker.ReportProgress(90, fileItems.ToList())` (although since it's not really 'progress' this would be best to return as the BGW's result and handle in the work complete handler..) – user2864740 Aug 21 '15 at 20:30
0

Call BiginInvoke directly on your list view control as follows:

files.BeginInvoke(new Action(() => files.ItemsSource = fileItems));
Deepak Bhatia
  • 1,090
  • 1
  • 8
  • 13