使用WPF UI线程应该始终确保STA的公寓模式,对吗?

时间:2010-03-05 08:43:44

标签: .net wpf invoke dispatcher sta

在我的WPF应用程序中,我与服务器异步通信。因此回调将不会在UI线程中运行,因为我需要在那里做一些WPF(创建InkPresenter对象),我需要它在UI线程上运行。嗯,实际上要求是它在STA公寓模式的线程上运行。我尝试使用STA模式创建一个新线程,但结果是UI线程无法访问InkPresenter,因为它是“由不同的线程拥有”。

我想在回调中做的是使用Dispatcher调用我需要STA的函数。这听起来像是正确的做法吗?我现在这样做,但它仍然失败。在我的回调函数中,我触发了以下函数,该函数现在尝试确保在UI线程上运行已寻址的函数。

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.. 
}

正如您从示例中看到的,我使用CheckAccess()来确保我只调用该函数(如果它尚未在UI线程上运行)。当我的回调调用此函数时,CheckAccess()始终为true,但Dispatcher.CurrentDispatcher.Thread.ApartmentState为MTA。为什么?我尝试删除CheckAccess()并始终执行Invoke,但ApartmentState仍然是MTA,并且创建InkPresenter失败。

任何人都可以解释一下我在这里做错了什么吗?我有错误的调度员吗?这是确保在UI线程上运行某些内容的正确方法吗?

2 个答案:

答案 0 :(得分:3)

我认为你混淆了2个要求。 WinForms和WPF主线程被标记为STA以启用COM调用(它们可能发生在控件内部。)

您的问题似乎是经典的“UI不是线程安全的”问题,应该通过调度触摸UI的部分来解决。

但是你不应该在CurrentDispatche上调用CheckAccess,而是调用你的目标:
 someControl.Dispatcher.CheckAccess

答案 1 :(得分:2)

我认为问题在于你使用了错误的Dispatcher。我使用过的一种经过验证的方法是传递代码执行的控件的Dispatcher。

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

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

如果无法以某种方式传递Dispatcher,您可以在属性或任何其他方法中公开它。我希望有所帮助。