1

This method call sits inside a class derived from DispatcherObject:

Dispatcher.Invoke(DispatcherPriority.Input, new ThreadStart(() =>
            {
                var exported = formatProvider.Export(original.Workbook);
                Workbook = formatProvider.Import(exported);
            }));

The method on the class is called by a backgroundworker in its DoWork delegate.

Workbook is Telerik's Workbook, as used by the RadSpreadsheetControl. Obviously, workbooks can only be accessed by the UI thread.

The above code throws an InvalidOperationException, saying

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

I don't really understand, as I thought that when invoking the actions with a Dispatcher, I would be calling it from the UI Thread, which is STA?

What am I missing here and how can this be fixed? Or should this work in general and the bug is somewhere else? What could be a reason then?

Marc
  • 12,706
  • 7
  • 61
  • 97

2 Answers2

4

TL;DR: You must create this DispatcherObject inside your UI thread, not in a worker.

DispatcherObject.Dispatcher, which you are marshalling the operation to, is set to Dispatcher.CurrentDispatcher at the time of the object's construction. If the object is not created inside your existing UI thread then the documented behavior of CurrentDispatcher is to create a new dispatcher object associated with the thread. Later on, Invoke tries to marshal the call to that thread (which is not STA) resulting in the error.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Ok, get it. What is a good practice to access the GUI thread in an object which hasn't been created by the main GUI thread? Can I - for instance - expose an application wide DispatcherObject which has been created in the GUI thread in a static field with a lock or something like that?? Thanks for your help! – Marc Dec 03 '13 at 16:26
  • @Marc: `Application.Current.Dispatcher` is such a static helper, most of the time it will work fine. – Jon Dec 03 '13 at 16:27
  • @Marc: There's a related answer of mine here, take a look: http://stackoverflow.com/q/10448987/50079 – Jon Dec 03 '13 at 16:32
3

It is not sufficient to use a class derived from DispatcherObject. You must use the Dispatcher from an existing UIElement created from XAML (or at least make sure, you create your class from inside the GUI thread where it picks the right Dispatcher).

JeffRSon
  • 10,404
  • 4
  • 26
  • 51
  • `UIElement` *is* a `DispatcherObject`, so the first sentence is really meaningless. The second sentence is also made equivalent to "use another object's dispatcher instead", in which case why make the current object a `DispatcherObject` to begin with? – Jon Dec 03 '13 at 16:23
  • Well - I edited that first part to make clearer what I meant. The second part is alternative, for when the OP doesn't have access to any existing UIElement where `Dispatcher.Invoke` is needed. – JeffRSon Dec 03 '13 at 16:26