43

In my WPF application I do some async communication (with server). In the callback function I end up creating InkPresenter objects from the result from server. This requires the running thread to be STA, which apparently it currently isn't. Therefore I get the following exception:

Cannot create instance of 'InkPresenter' defined in assembly [..] The calling thread must be STA, because many UI components require this.

Currently my async function call is like this:

public void SearchForFooAsync(string searchString)
{
    var caller = new Func<string, Foo>(_patientProxy.SearchForFoo);
    caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null);
}

How can I make the callback - which will do the InkPresenter creation - be STA? Or invoke the XamlReader parsing in a new STA thread.

public void SearchForFooCallbackMethod(IAsyncResult ar)
{
    var foo = GetFooFromAsyncResult(ar); 
    var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter; // <!-- Requires STA
    [..]
}
stiank81
  • 25,418
  • 43
  • 131
  • 202
  • Whatever happened to `[STAThread]` before a method? Not always appropriate but very easy. Maybe it didn't come out till 2011? I haven't used it since 2011 that I recall... – ebyrob Jul 06 '17 at 03:41
  • Adding [STAThread] before the method worked for me. Thank you, ebyrob! :-) – Kim Homann May 28 '21 at 14:47

5 Answers5

59

You can start STA Threads like so:

    Thread thread = new Thread(MethodWhichRequiresSTA);
    thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
    thread.Start(); 
    thread.Join(); //Wait for the thread to end

The only problem is that your result object must be passed along somehow.. You can use a private field for that, or dive into passing along parameters into threads. Here I set the foo data in a private field and start up the STA Thread to mutate the inkpresenter!

private var foo;
public void SearchForFooCallbackMethod(IAsyncResult ar)
{
    foo = GetFooFromAsyncResult(ar); 
    Thread thread = new Thread(ProcessInkPresenter);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join(); 
}

private void ProcessInkPresenter()
{
    var inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter;
}

Hope this helps!

Arcturus
  • 26,677
  • 10
  • 92
  • 107
  • Thanks :) Let me know if it will fix the problem. We use this technique to generate PNG images of our Xaml controls on the server! – Arcturus Mar 04 '10 at 10:16
  • Seems to fix the problem, but just met another problem. *Sigh*.. Will mark as accepted as soon as I get everything working here.. Thx! – stiank81 Mar 04 '10 at 10:38
  • @Arcturus: what is `MethodWhichRequiresSTA` just a method? my code will not compile if I put a method there since it needs its arguments – Saher Ahwal Jul 27 '12 at 04:43
  • Can you please provide an example of method MethodWhichRequiresSTA? – James Dec 18 '14 at 13:02
  • Sorry for a late post - but what is GetFooFromAsyncResult? – curiousity Nov 18 '15 at 09:31
13

You can use the Dispatcher class to execute the method call on the UI-Thread. The Dispatcher provides the static property CurrentDispatcher to get the dispatcher of a thread.

If your object of the class, that creates the InkPresenter, is created on the UI-Thread, then the CurrentDispatcher method returns the Dispatcher of the UI-Thread.

On the Dispatcher you can call the BeginInvoke-method to call the specified delegate asynchronously on the thread.

Martin
  • 16,093
  • 1
  • 29
  • 48
Jehof
  • 34,674
  • 10
  • 123
  • 155
  • 1
    Dispatcher.Invoke or BeginInvoke is the way to go. Much simpler than the accepted solution – GameAlchemist Jun 09 '13 at 16:39
  • Should be wary of using `Dispatcher.CurrentDispatcher` unless you are certain you are running on a UI thread that will be processing `DispatcherOperation`s. `CurrentDispatcher` will create a dispatcher for the current thread if it doesn't already have one, even if the current thread is MTA. I usually find it better to use the `Dispatcher` property on the control I want to update. – Brian Reichle Jul 15 '19 at 07:51
3

It should be good enough to call it on the UI thread. Therefore, use a BackgroundWorker and on the RunWorkerAsyncCompleted, you can then do the creation of the inkPresenter.

Kyle Rosendo
  • 25,001
  • 7
  • 80
  • 118
  • You're right. Problem is that the callback isn't run on the UI thread. The UI thread is run with STA, so running it on the UI thread should solve this for me. – stiank81 Mar 04 '10 at 09:47
1

It's a bit of a hack, but I would use XTATestRunner So your code will look like:

    public void SearchForFooAsync(string searchString)
    {
        var caller = new Func<string, Foo>(_patientProxy.SearchForFoo);
        caller.BeginInvoke(searchString, new AsyncCallback(SearchForFooCallbackMethod), null);
    }

    public void SearchForFooCallbackMethod(IAsyncResult ar)
    {
        var foo = GetFooFromAsyncResult(ar); 
        InkPresenter inkPresenter;
        new XTATestRunner().RunSTA(() => {
            inkPresenter = XamlReader.Parse(foo.Xaml) as InkPresenter;
        });
    }

as a bonus it's possible to catch exceptions thrown in STA (or MTA) thread like this:

try
{
    new XTATestRunner().RunSTA(() => {
        throw new InvalidOperationException();
    });
}
catch (InvalidOperationException ex)
{
}
mouldycurryness
  • 69
  • 1
  • 11
alex
  • 11
  • 1
  • 2
1

I have just used the following to get clipboard content from the STA thread. Thought I would post to maybe help someone in the future...

string clipContent = null;
Thread t = new Thread(
    () =>
    {
        clipContent = Clipboard.GetText();
    });
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();

// do stuff with clipContent

t.Abort();
mouldycurryness
  • 69
  • 1
  • 11