我必须使用另一个应用程序域中的功能。结果应显示在用户控件中。
我有这样的东西:
var instance = domain.CreateInstanceFromAndUnwrap(...);
instance.Foo(myWpfUserControl as ICallback);
Foo(ICallback itf) {
itf.SetData("...");
}
WpfUserControl.SetData(string data)
{
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(...)
...
}
我不得不把[Serializable]属性放在WpfUserControll类上,并实现序列化构造函数和ISerializable接口,但现在我收到了异常:
The calling thread must be STA because many UI components require this
从UserControl()构造函数引发的
我该怎么做才能避免这种情况?提前谢谢!
====================================
解决方案
正如@Al所注意到的,当涉及到跨应用程序域调用时,我的用户控件必须序列化。现在我传递代理,它实现ICallback接口。代理被标记为具有Serializable特性。
代理实现应该完全不了解用户控件,因为应该再次尝试反序列化用户控件实例。当我试图通过接口从用户控制中抽象代理时,它并没有帮助。当我试图将接口传递给代理(由用户控制实现)时,发生了同样的异常。
最后,我用队列/信号量解耦了代理和用户控制。队列由一个工作线程监控,该线程为用户控制的调用提供了支持
p.s.此队列应从"MarshalByObjectRef"继承。
如果异常来自构造函数,则表示您没有从UI线程创建此控件实例。这可能很好,但您必须在线程启动前通过调用线程对象上的.SetApartmentState(ApartmentState.STA)
来确保线程是STA线程。
这也意味着你必须在线程对象启动之前访问它,所以你不能在线程池线程上这样做。
不过,避免该问题的最佳方法可能是在主UI线程上创建控件,然后使用Dispatcher(或UiSchedur上的Task)分配Text值。这样,如果主线程需要设置、获取或绑定到控件,也可以避免出现问题,因为如果控件是在另一个线程上创建的,则会导致跨线程异常
如果可能的话,我建议不要用这种方式串联控制。这样做将生成一个新对象,该对象不会附加到任何面板或类似面板上,并且原始控件不会更新。遗憾的是,您不能从MarshalByRefObject中继承它,因为它只会传递对另一个域的引用,从而消除序列化。
如果可以,请单独调用Foo,然后将结果传递给原始Appdomain 中的SetData