问题描述
这是我的第一篇文章,实际上我通常解决所有我的问题与真棒
post数据库,你可以找到这里。但我现在卡住了:
我正在处理一个项目,下面的MVVM包括一个COM对象。
正如我在研究期间读到的,我理解COM对象只能从创建它的线程访问。我的COM对象实现以下接口
interface IComUpdate
{
void Update
}
所以当我创建我的COM对象时,每次有更新不知道什么时候,它的随机)COM服务器将调用我实现的COM对象类的 Update()
。
我的目标是创建一个不同的线程,命名一个COM对象线程,其中COM对象独立存在于我的UI线程,所以每次有一个更新,我处理它在不同的线程比UI线程。 p>
实际上它是工作的:
在我的ViewModel开始,我创建一个特定对象的集合。 p>
这个对象叫做 ModelObj
,是模型的一部分,定义了一个静态构造函数,从初始化一些变量,为COM对象创建和启动一个新的线程:
线程t = new System.Threading.Thread ()=>
{
System.Threading.Thread.CurrentThread.Name =COM对象的线程;
IComUpdate myComObj;
myComObj =(IComUpdate)Activator.CreateInstance(blabla);
Application.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
它实际上效果非常好,在 Update $ c>我的COM对象的实现,我实际上看到的线程是一个刚刚创建,而不是UI线程。
现在的问题是这个:this ModelObj
我创建实现 INotifyPropertyChanged
接口。
以下:每次COM对象接收更新,我处理来自COM对象线程的数据,并更新我的 ModelObj
实例从这个线程的一些属性,因此这些属性然后提高我的 ModelObj
的属性更改,UI线程将更新用户界面。
花费太多时间,我可能会错过一些 Update()
出现在屏幕上,但COM对象将它们记录在我的 ModelObj
实例,所以它不是非常重要的UI捕获所有的更新,我只是不想要COM对象必须等待UI更新,以便再次调用。
我读了许多帖子,然后认为我的 RaisePropertyChanged(property)
会失败。
实际上即使在COM对象的线程中, RaisePropertyChanged
成功执行,因此跟踪我的代码,我看到它切换到我的ViewModel程序集
//这里我仍然在我的COM对象的线程中!
base.NotifyOfPropertyChange< string>(()=> this.property)
那么UI更新。
注意:我使用Caliburn Micro在WPF中的View和ViewModel之间进行绑定。 所以我不能追踪这个 我可以说是我的COM对象线程等待UI更新到下一个在 我希望我的COM对象线程更新我的 关于此行为的想法? 非常感谢。 #### UPDATE ## 感谢大家提供快速解答。 我确实是Zdeslav Vojkovic : 您应该总是从GUI线程更新GUI 为了完整性,这里是我做的: 因为我的View是完整的WPF没有代码后面我没有任何控件或窗体被调用BeginInvoke从,所以在我的ModelObj的静态构造函数,我构建了一个不可见的控件从UI线程只是为了能够调用BeginInvoke就可以了。 所以我宣布: 然后在我的对象的静态构造函数中执行: 通过这种方式初始化委托: 然后,我只是这样使用: 方法是: 一切正常! 这不是真的。它取决于对象的公寓模型,但通常你可以从任何线程访问它,它将被调用在同一个线程或封送到正确的线程。 我相信你的问题是你从后台线程更新GUI是主要的no-no。你应该总是从GUI线程更新GUI。当你更新模型对象时,它仍然发生在后台线程和 你需要通过使用这样的东西(WinForms,而不是WPF)同步模型更新到GUI线程 - 在WPF中,你应该使用 私人代理void ExecuteActionHandler(Action action); 有,我已提供了其他详细信息。 This is my first post here as actually i usually solve all my issue with the awesomepost database you can find here. But I'm actually stuck right now: I'm working on a project following the MVVM including a COM object.As I read during my research, I understand that the COM object is only accessible from the thread which created it. My COM object implements the following interface So when I create my COM object, each time there is an update (I dont know when, its random) the COM Server will call the My goal was to create a different thread, naming a COM object thread, where the COM object exist independantly of my UI Thread, so everytime there is an update, I handle it in a different thread than the UI Thread. Actually it is working: At the Beginning of my ViewModel I create a collection of a specific object. This object, lets call it It actually works very well, in the Now the issue is this: this My thinking was the following: each time the COM object receives an update, I handle data from the COM object thread, and update some property of my If the UI update takes too much time, I might miss some I read tons of posts and thought then that my Actually even in the COM object's thread, the and then the UI Update. Note: I'm using Caliburn Micro for binding between my View in WPF and my ViewModel. So I can't trace after this What I can say is that my COM object thread waits for the UI to update to get to the next instruction after my I want my COM object thread to update my Does someone got an idea about this behaviour? Thank you very much. ####UPDATE#### Thanks everyone for such quick answers. I did actually as Zdeslav Vojkovic suggested : You should always update GUI from GUI thread For completeness here is how I did: Because my View is full WPF with no code behind i dont have any controls or form to be call BeginInvoke from, so in the static constructor of my ModelObj, I built an invisible Control from the UI Thread just to be able to call BeginInvoke on it. So i declared it : and then did this in the static constructor of my Object: in the normal constructor i Initialize the delegate this way: Then after i just use it this way: With the method being: Everything works fine ! this is not true. It depends on objects apartment model, but usually you can access it from any thread and it will be either called on same thread or marshaled to proper thread. I belive that your problem is that you update GUI from background thread which is a major no-no. You should always update GUI from GUI thread. When you update your model object, it still happens on background thread and event of You need to synchronize model update to GUI thread by using something like this (WinForms, not WPF - in WPF you should use private delegate void ExecuteActionHandler(Action action); There is another question with similar problem and I have provided additional details there. 这篇关于多线程COMObject和UI线程(C#)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
base.NotifyOfPropertyChange< string>(()=> this.property)
。也许Caliburn处理线程切换,这不是我的问题。
RaisePropertyChanged(property)
之后的指令,因此它与UI线程完成整个工作完全相同。
ModelObj
,它会发送给UI发送消息更新(因为这个 ModelObj
已更改),并立即继续立即,而不知道UI是否真的更新。
public static Control mInvokeControl;
delegate void MyDelegate();
private MyDelegate _NotifyDelegate;
mInvokeControl = new Control();
mInvokeControl.CreateControl();正常构造函数中的
_NotifyDelegate = new MyDelegate(this.NotifyByInvoke);
ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate);
public void NotifyByInvoke()
{
RaisePropertyChanged(Update);
}
$ b
INotifyPropertyChanged
接口在该线程上触发的事件。
frm.Dispatcher.BeginInvoke
但问题是一样的):
public static void ExecuteOnUiThread(this Form form,Action action)
{
if(form.InvokeRequired){//我们不在UI线程
//调用或BeginInvoke,取决于你需要
//但你说'并继续立即'所以BeginInvoke它是
form.BeginInvoke(new ExecuteActionHandler(ExecuteOnUiThread),action);
}
else {//我们在UI线程上,所以只是执行动作
action();
}
}
interface IComUpdate
{
void Update();
}
Update()
of the COM object class I did implement.ModelObj
, is part of the model and defines a static constructor in which the application, apart from initializing some variables, creates and starts a new thread for the COM object:Thread t = new System.Threading.Thread(() =>
{
System.Threading.Thread.CurrentThread.Name = "Thread of COM Object";
IComUpdate myComObj;
myComObj = (IComUpdate)Activator.CreateInstance(blabla);
Application.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
Update()
implementation of my COM object, I actually see that the thread is the one just created and not the UI thread.ModelObj
I create implements the INotifyPropertyChanged
interface.ModelObj
instance from this thread, so these properties will then raise the property change of my ModelObj
and the UI thread will update the User Interface.Update()
to appear on the screen but the COM object will have them recorded in my ModelObj
instance so it is not very important that the UI catch all the updates, I just didnt want the COM object to have to wait for the UI to be updated to be called again.RaisePropertyChanged("property")
would fail.RaisePropertyChanged
successfully executes, so tracing my code, I see it switches to my ViewModel assembly where I do// Here I'm still in the thread of my COM object!
base.NotifyOfPropertyChange<string>(() => this.property)
base.NotifyOfPropertyChange<string>(() => this.property)
. Maybe Caliburn handles the thread switch, this is not really my issue.RaisePropertyChanged("property")
, so it's exactly the same as if the UI thread did the whole work.ModelObj
which will send to send the UI a message to update (because some fields of this ModelObj
have changed) and continue immediatly, without knowing if the UI actually updates or not.public static Control mInvokeControl;
delegate void MyDelegate();
private MyDelegate _NotifyDelegate;
mInvokeControl = new Control();
mInvokeControl.CreateControl();
_NotifyDelegate = new MyDelegate(this.NotifyByInvoke);
ModelObj.mInvokeControl.BeginInvoke(this._NotifyDelegate );
public void NotifyByInvoke()
{
RaisePropertyChanged("Update");
}
INotifyPropertyChanged
interfaces fires on that thread.frm.Dispatcher.BeginInvoke
but the problem is the same):public static void ExecuteOnUiThread(this Form form, Action action)
{
if (form.InvokeRequired) { // we are not on UI thread
// Invoke or BeginInvoke, depending on what you need
// but you said ' and continue immediatly' so BeginInvoke it is
form.BeginInvoke(new ExecuteActionHandler(ExecuteOnUiThread), action);
}
else { // we are on UI thread so just execute the action
action();
}
}