8

SolidColorBrush我在一些非 GUI 线程上创建了一个,并想将它传递给一个 GUI 线程来显示它,但我得到了InvalidOperationException:(The 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; }
    }
}
4

3 回答 3

8

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

于 2013-05-06T11:51:47.890 回答
1

您需要使用 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 });
  }
}

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

检查这些链接:

如何从 C# 中的另一个线程更新 GUI?在此处输入链接描述

在此处输入链接描述

于 2013-05-06T06:56:56.310 回答
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);
    }
}

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

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

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

于 2013-05-06T07:53:57.903 回答