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

发布于 2024-08-24 03:36:24 字数 1144 浏览 11 评论 0原文

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

游魂 2024-08-31 03:36:24

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

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

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

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

儭儭莪哋寶赑 2024-08-31 03:36:24

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

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

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

如果不知何故无法传递调度程序,您可以在属性或任何其他方法中公开它。我希望这有帮助。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文