2
votes

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?

2

2 Answers

3
votes

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

2
votes

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.