WinForms 中的托管线程如何使用“this->”在回调函数中访问表单元素(更改值等)

Posted

技术标签:

【中文标题】WinForms 中的托管线程如何使用“this->”在回调函数中访问表单元素(更改值等)【英文标题】:Managed Thread from WinForms how to use "this->" to access elements of the form (change values and etc) in the callback function 【发布时间】:2012-03-31 20:25:33 【问题描述】:

嗯,我在 Visual Studio 2008 的托管 C++/Cli 中有这段代码,我希望能够访问线程函数回调中的 windows 窗体项,但我不能,它会产生错误。 还有另一种方法吗?能够通过使用线程函数回调来修改 WinForms 类的方法内的 GUI 内容?

这个例子展示了我想要做什么。

我需要使用线程,因为我希望表单中的其他内容可以访问,并且不使用线程,一切都会冻结,直到一切都完成,并且它调用的“登录”函数需要一些时间,因为它执行 HTTP 请求。在这个 HTTP 请求之后,我在表单元素中设置了我从中获得的值。

void Login()
    this->btn_next->Enabled = false;

    this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
        if(this->clb_contas->CheckedItems->Count <= 0)
             //...
        


System::Void test_login_Click(System::Object^  sender, System::EventArgs^  e) 
    ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::Login);
    Thread^ t = gcnew Thread(start);
    t->Start();

有人知道我该怎么做吗?如果您认为这无法完成,并且您想提出一些建议以在执行此过程时使 GUI 可用,我愿意接受建议。

我希望我说得足够清楚。 提前致谢。

【问题讨论】:

启用属性分配的一个简单解决方法是将它们移动到 test_login_Click() 方法。但是您需要重新打开它们。最好使用 BackgroundWorker 的 RunWorkerCompleted 事件来完成。我不是已经提到了吗? +1 最终,BackgroundWorker 是要走的路。这是一个很好的裸线包装,专为这种场景量身定制。基本思想仍然存在:您不应该(并且在大多数情况下您不能)从非 UI 线程触摸 UI。查看here 的使用示例。 【参考方案1】:

所有与 UI 相关的代码都应在 UI 线程上执行。在您的情况下,这意味着只有您用 //... 表示的代码应该在单独的线程上运行。在自己的方法中提取长时间运行的代码并将该方法传递给ThreadStart 而不是Login()。然后,您需要为工作线程安排一种方法,以便在完成时通知 UI 线程。

更新:

这是一个关于如何修改代码的粗略示例。如果它足够复杂,我更愿意在它自己的类中提取长时间运行的操作,但我想你明白了。

BeginInvoke 的调用确保LongRunningOperationComplete 将在表单的UI 线程上执行。您可以使用相同的方法调用更新 UI 以指示进度的其他方法,即使在耗时的操作仍在运行时也是如此。如果这些方法需要更多参数,您可以使用适当的签名创建不同的委托,并将这些参数在调用中传递给BeginInvoke。请参阅here 了解如何执行此操作。

// Same signature as LongRunningOperationComplete
delegate void MyInvokeDelegate();

void LongRunningOperation() 
  for (int i=0; i < 100; i++) 
    Thread::Sleep(100);
    // The actual work that you're doing
  

  // Operation complete. Update UI.
  this->BeginInvoke(gcnew MyInvokeDelegate(this, &Login_Test::LongRunningOperationComplete)); 


void LongRunningOperationComplete() 
  this->btn_next->Enabled = true;
  this->login_accounts_facebook->Enabled = true;


System::Void StartMyLongRunningOperation() 
  ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::LongRunningOperation);
  Thread^ t = gcnew Thread(start);
  t->Start();


void Login() 
  this->btn_next->Enabled = false;

  this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
  if(this->clb_contas->CheckedItems->Count <= 0)
    StartMyLongRunningOperation();
  


System::Void test_login_Click(System::Object^  sender, System::EventArgs^  e) 
  Login();

【讨论】:

问题是带有 //... 的部分具有更新 UI 的代码,所以如果我在线程中删除该部分,我将遇到同样的问题,因为它会必须使用来自 //... http 连接的新信息更新 UI 元素。 这就是为什么我提到你需要为你的工作线程安排一种与 UI 线程通信的方式。 BackgroundWorker 是要走的路。 :D 这些天我都在学习它,这是正确的。 :D

以上是关于WinForms 中的托管线程如何使用“this->”在回调函数中访问表单元素(更改值等)的主要内容,如果未能解决你的问题,请参考以下文章

在默认 AppDomain 中的 Winforms 中托管 WPF 用户控件的弹出窗口中的选项卡导航损坏

VB.NET(WinForms)中带参数的安全线程池队列

检测是不是在 WPF 和 Winforms 中的 UI 线程上

WinForms 中的每个表单都有自己的线程吗?

跨线程Winforms控件编辑[重复]

从主机 WinForms 程序访问自托管 WCF 服务