tags:

views:

583

answers:

3

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
    [..]
}
+2  A: 

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 Rozendo
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
+4  A: 

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
Looks very helpful - thx! Will try to apply this to my code.
stiank81
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
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
+1  A: 

You can use the Dipatcher 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.

Jehof