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

时间:2013-05-06 06:53:09

标签: c# wpf multithreading

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

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


其他详情:

我在一些静态类中初始化一些静态委托,以允许从业务层向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; }
    }
}

3 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:1)

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

使用

  

Control.Invoke

  

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

答案 2 :(得分:0)

您应该使用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内容。