views:

543

answers:

2

In my WPF application I communicate asynchronously with a server. The callback will hence not be run in the UI thread, and as I need to do some WPF stuff there (create InkPresenter object) I need it to be run on the UI thread. Well, actually the requirement is that it is run on a thread with STA apartment mode. I tried creating a new thread with STA mode, but the result was that the UI thread couldn't access the InkPresenter as it was "Owned by a different thread".

What I want to do in the callback is to use the Dispatcher to Invoke my function that requires STA. Does this sound like the right approach? I do this now, but it still fails. In my callback function I trigger the following function, which now tries to ensure that the addressed function is run on the UI thread.

private void UpdateAnnotationsForCurrentFrameCollection()
{
    if (Dispatcher.CurrentDispatcher.CheckAccess())
    {
        DoSomethingIncludingInkPresenter();
    }
    else
    {
        Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal,
           new Action(DoSomethingIncludingInkPresenter));
    }
}

private void DoSomethingIncludingInkPresenter()
{
    var inkPresenter = XamlReader.Parse(_someXamlString) as InkPresenter;
    // Do something with the inkPresenter.. 
}

As you see from the sample I use CheckAccess() to ensure that I only Invoke the function if it isn't already run on the UI thread. When my callback calls this function CheckAccess() is always true, but Dispatcher.CurrentDispatcher.Thread.ApartmentState is MTA. Why? I tried removing CheckAccess() and always doing Invoke, but the ApartmentState remains MTA, and creating the InkPresenter fails.

Can anyone please explain me what I'm doing wrong here? Do I have the wrong Dispatcher or something? Is this the right approach to ensuring that something is run on the UI thread?

+3  A: 

I think you are confusing 2 requirements. WinForms and WPF main threads are marked STA to enable COM calls (and they could be happen inside controls).

Your problem seems to be the classic "the UI is not thread-safe" issue, and should be solved by dispatching the parts that touch the UI.

But you should not call CheckAccess on the CurrentDispatche but on your target:
someControl.Dispatcher.CheckAccess

Henk Holterman
Thanks! Ehr.. Can I bother you to elaborate? :-)
stiank81
I was still editing, wrong dispatcher for CheckAccess
Henk Holterman
Thanks. This is however in a ViewModel, so there are no controls to take the dispatcher from. Is there no such thing as "the general dispatcher for the UI thread"? Maybe the answer is that I should never put WPF objects like InkPresenter in the ViewModel..? But can I? And if so - how do I get the right Dispatcher?
stiank81
That has been asked here, I can't find it right now. Out of time.
Henk Holterman
Ok, no problem. Thanks for your help. I got an idea of something new to test now..
stiank81
@Stian: I found this one but it has been discussed more often: http://stackoverflow.com/questions/2354438
Henk Holterman
+1  A: 

I think the problem is that you are using the wrong Dispatcher. One of the tried and true method I've used is to pass the Dispatcher of the control in which the code executes.

private void SomeMethod(Dispatcher dispatcher)
{
  DoOtherThingsThatCanDoMTA();

  dispatcher.Invoke(new Action(()=>
  {
    DoSomethingThatRequiresSTA();
  }));
}

If somehow it's not possible to pass the Dispatcher you can expose it in a property or any other methods. I hope that helps.

Jaya Wijaya
Thanks. This would probably often work, but it doesn't seem like I have access to the right Dispatcher. Will try a different approach, but I appreciate your input on this.
stiank81
It's not an MTA/STA issue.
Henk Holterman