【问题标题】:How do I pass a WPF object that was created in thread X, to thread Y?如何将在线程 X 中创建的 WPF 对象传递给线程 Y?
【发布时间】:2013-05-06 06:53:09
【问题描述】:

我在一些非 GUI 线程上创建了一个 SolidColorBrush,并想将它传递给一个 GUI 线程来显示它,但我得到了 InvalidOperationException: The calling thread cannot access this object because a different thread owns it.(即使我尝试 Freeze(); 它) .如何将在线程 X 中创建的对象传递给线程 Y?

我知道我可以使用 Dispatcher 在 GUI 线程中创建这个 SolidColorBrush 对象,但这会使一切复杂化...我想在工作线程中创建它。


其他细节:

我在某个静态类中初始化了一些静态委托,以允许从业务层向 GUI 发送消息:

public static class Gui{
    private static PrintMethodDelegate _printMethod;
    public static void InitializeGuiInterface(PrintMethodDelegate printMethod){
        _printMethod = printMethod;
    }
    public static void Print(GuiMessage data) { _printMethod(data); }
}

初始化(在 GUI 线程中):

Gui.InitializeGuiInterface(_messagesToUserHandler.PrintMessage);

然后在另一个(非 gui)线程中,我使用它:

Gui.Print(new GuiMessage(testDescription) { Foreground = new SolidColorBrush(someColor) });

GuiMessage 是:

public class GuiMessage {
    public string Msg { get; set; }

    private SolidColorBrush _foregroundBrush;
    public SolidColorBrush Foreground
    {
        get { return _foregroundBrush; }
        set { _foregroundBrush = value; }
    }
}

【问题讨论】:

  • 您是否尝试将错误消息写给谷歌?它返回我们在这里可能给出的所有答案。
  • 我想简短的回答是你不能。所有与 GUI 相关的控件/工件都应在 UI 线程上创建和处理。
  • @I4V:是的,我试过了。谢谢你的建议。
  • @EbenRoux:没有办法吗?没有共享内存(类/C#-API/属性)设施来做到这一点?这不是很奇怪吗?
  • freeze 可以在另一个线程中创建 wpf 资源,然后可以将元素传递给另一个线程或 gui 线程。

标签: c# wpf multithreading


【解决方案1】:

如果您freeze 可以在另一个线程中创建 wpf 资源,然后可以将元素传递给另一个线程或 gui 线程。 请记住,只能通过制作副本并使用该副本来修改曾经冻结的对象。您不能冻结附加了绑定或动画的对象。

【讨论】:

  • 就是这样。我立即冻结了属性设置器中的Brush。所以这不是多线程限制......它是为这种对象设计的(Freezables)
【解决方案2】:

您需要使用 Delegate 来安全地调用控件。

使用

控制调用

Control.BeginInvoke

为此目的。

private delegate void SetControlPropertyThreadSafeDelegate(Control control, string propertyName, object propertyValue);

public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
{
  if (control.InvokeRequired)
  {
    control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
  }
  else
  {
    control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
  }
}

如果你不使用委托来安全调用它们,你会得到异常。

检查这些链接:

How to update the GUI from another thread in C#?enter link description here

enter link description here

【讨论】:

  • 我看到了,但是如果 Brush 首先是在非 GUI 线程中创建的,这对我没有帮助。我只解决了你想在 GUI 线程中创建的对象上调用的问题,反之亦然。
【解决方案3】:

您应该使用Dispatcher

您可以创建一个类,该类将保存在主线程上创建的调度程序,并通过您的容器将其注入到需要与主线程交互的后台线程上执行的任何类。

public interface IUiDispatcher
{
    Dispatcher Dispatcher { get; }
}

public class UiDispatcher : IUiDispatcher
{
    public UiDispatcher()
    {
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA
            && !Thread.CurrentThread.IsBackground
            && !Thread.CurrentThread.IsThreadPoolThread)
        {
            this.Dispatcher = Dispatcher.CurrentDispatcher;
        }
        else
        {
            throw new InvalidOperationException("Ui Dispatcher must be created in UI thread");
        }
    }

    public Dispatcher Dispatcher { get; set; }
}

public class ExecutedOnABackgroundThread
{
    IUiDispatcher uidispatcher;

    public ExecutedOnABackgroundThread(IUiDispatcher uidispatcher)
    {
        this.uidispatcher = uidispatcher;
    }

    public void Method()
    {
        // Do something on the background thread...
        // ...

        // Now we need to do something on the UI
        this.uidispatcher.Dispatcher.BeginInvoke(new Action(delegate
        {
            // Do something
        }), null);
    }
}

在您确定自己在 UI 线程上的某个时间点创建 UiDispatcher 的实例,例如在应用程序的初始化期间。使用您的依赖注入容器,确保只会创建此类的一个实例并将其注入到需要它的任何其他类中,并使用它来创建/操作您的 UI 组件。

我选择了代码来检查UiDispatcher的构造函数是否在this answer的主线程中执行。

问题是你不能在 UI 线程上使用在不同线程上创建的东西。所以你需要你的后台线程将任何涉及 UI 的东西委托给主 UI 线程。

【讨论】:

  • 正如我所说,“我知道我可以使用Dispatcher 在GUI 线程中创建这个SolidColorBrush 对象,但这会使一切变得复杂......我想在工作线程中创建它”
  • 是的,我读过。但我不知道什么是复杂的。特别是如果您只需要使用this.dispatcher.BeginInvoke。你如何在 UI 线程上使用在后台线程上创建的东西? There's no other way than delegating the creation to the UI thread.
  • 我在谷歌上搜索了一下,并认为这无法完成是荒谬的。您不能将数据从某个内存段复制到其他内存段吗?克隆对象?这没有道理。为什么存在这个限制?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
相关资源
最近更新 更多