2

在我的 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 线程上运行某些东西的正确方法吗?

4

2 回答 2

3

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

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

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

于 2010-03-05T08:49:17.320 回答
2

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

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

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

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

于 2010-03-05T09:12:46.290 回答