C++/CLI WinForms:BeginInvoke 错误
C++/CLI WinForms: BeginInvoke Error
我无法找出这个错误的原因:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
这是我的(剥离的)代码:
private: delegate void MyDelegate(Object^ openFileDialog1);
ListView^ myDelegate;
private: void import_links(Object^ openFileDialog1) {
myDelegate = (gcnew System::Windows::Forms::ListView());
myDelegate->BeginInvoke( gcnew MyDelegate( this, &Form1::import_links ), openFileDialog1);
//do some work here
}
private: System::Void Import_LinkClicked(System::Object^ sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs^ e) {
OpenFileDialog^ openFileDialog1 = gcnew OpenFileDialog;
if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK )
{
Thread^ importThread = gcnew Thread(gcnew ParameterizedThreadStart(this,&Form1::import_links));
importThread->Start(openFileDialog1);
}
}
请告诉我解决方法。
myDelegate = (gcnew System::Windows::Forms::ListView());
此语句的基本问题:
- 不是委托,是ListView对象
- 控件与创建它们的线程有很强的关联。像你这样在工作线程上创建控件永远不会正确
- 一个控件需要父级才能变得可见和有用,它没有
- 控件需要线程 运行 调度程序循环,以便它可以获取消息。这样的线程必须调用Application::Run()。你的工作线程没有
- 控件的基础 window 是在需要时延迟创建的,以使其可见。由于它没有父对象且不可见,因此还没有创建 window 的任何必要。所以它不会有一个有效的句柄属性,因为异常告诉你
- BeginInvoke() 确保调用的代码 运行 在拥有控件的线程上。因为它是拥有它的工作线程,所以 BeginInvoke() 永远不能真正调用到另一个线程
您已经引用了正确线程所拥有的对象。是this
。所以正确的代码看起来像:
void import_links(Object^ openFileDialog1) {
if (this->InvokeRequired) {
this->BeginInvoke( gcnew MyDelegate( this, &Form1::import_links ), openFileDialog1);
}
else {
//do some work here
}
}
但请注意最终的谬误,您创建了一个工作线程,唯一 它所做的事情是调用 this->BeginInvoke()
。这需要几分之一微秒。创建一个线程来做这么少的工作是没有用的。
重构您的代码,使用 BackgroundWorker。让它的 DoWork 事件处理程序 只 做那种昂贵的事情,比如导入文件。让它的 RunWorkerCompleted 事件 only 做那种需要在 UI 线程上发生的事情,比如显示导入的结果和隐藏 "I'm working on it" 通知.
我无法找出这个错误的原因:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
这是我的(剥离的)代码:
private: delegate void MyDelegate(Object^ openFileDialog1);
ListView^ myDelegate;
private: void import_links(Object^ openFileDialog1) {
myDelegate = (gcnew System::Windows::Forms::ListView());
myDelegate->BeginInvoke( gcnew MyDelegate( this, &Form1::import_links ), openFileDialog1);
//do some work here
}
private: System::Void Import_LinkClicked(System::Object^ sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs^ e) {
OpenFileDialog^ openFileDialog1 = gcnew OpenFileDialog;
if ( openFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK )
{
Thread^ importThread = gcnew Thread(gcnew ParameterizedThreadStart(this,&Form1::import_links));
importThread->Start(openFileDialog1);
}
}
请告诉我解决方法。
myDelegate = (gcnew System::Windows::Forms::ListView());
此语句的基本问题:
- 不是委托,是ListView对象
- 控件与创建它们的线程有很强的关联。像你这样在工作线程上创建控件永远不会正确
- 一个控件需要父级才能变得可见和有用,它没有
- 控件需要线程 运行 调度程序循环,以便它可以获取消息。这样的线程必须调用Application::Run()。你的工作线程没有
- 控件的基础 window 是在需要时延迟创建的,以使其可见。由于它没有父对象且不可见,因此还没有创建 window 的任何必要。所以它不会有一个有效的句柄属性,因为异常告诉你
- BeginInvoke() 确保调用的代码 运行 在拥有控件的线程上。因为它是拥有它的工作线程,所以 BeginInvoke() 永远不能真正调用到另一个线程
您已经引用了正确线程所拥有的对象。是this
。所以正确的代码看起来像:
void import_links(Object^ openFileDialog1) {
if (this->InvokeRequired) {
this->BeginInvoke( gcnew MyDelegate( this, &Form1::import_links ), openFileDialog1);
}
else {
//do some work here
}
}
但请注意最终的谬误,您创建了一个工作线程,唯一 它所做的事情是调用 this->BeginInvoke()
。这需要几分之一微秒。创建一个线程来做这么少的工作是没有用的。
重构您的代码,使用 BackgroundWorker。让它的 DoWork 事件处理程序 只 做那种昂贵的事情,比如导入文件。让它的 RunWorkerCompleted 事件 only 做那种需要在 UI 线程上发生的事情,比如显示导入的结果和隐藏 "I'm working on it" 通知.