如何将在线程X中创建的WPF对象传递给线程Y ?

本文关键字:线程 对象 WPF 创建 | 更新日期: 2023-09-27 18:12:20

我在一些非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; }
    }
}

如何将在线程X中创建的WPF对象传递给线程Y ?

您可以在另一个线程中创建wpf资源,如果您冻结它们,之后元素可以传递给另一个线程或gui线程。记住,一旦冻结的对象只能通过复制并使用该副本来修改。你不能冻结带有绑定或动画的对象

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

使用

控制。调用

控制。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 });
  }
}

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

查看这些链接:

如何从c#中的另一个线程更新GUI ?在这里输入链接描述

输入链接描述

您应该使用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);
    }
}

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

我从这个答案中选择代码来检查UiDispatcher的构造函数是否在主线程中执行。

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