Управляемый поток из WinForms, как использовать this-› для доступа к элементам формы (изменение значений и т. д.) в функции обратного вызова

Ну, у меня есть этот код в моем управляемом C++/Cli в Visual Studio 2008, я хочу иметь доступ к элементам форм Windows внутри обратного вызова функции потока, и я не могу, он генерирует ошибку. Есть ли другой способ сделать это? чтобы иметь возможность изменять материал GUI внутри метода класса WinForms с помощью обратного вызова функции Thread?

Этот пример показывает, что я хочу сделать.

Мне нужно использовать поток, потому что я хочу, чтобы другие вещи в формах были доступны, и без использования потоков все просто зависает, пока все не будет сделано, и функция «Вход», которую она вызывает, занимает некоторое время, потому что она выполняет 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();
}

Кто-нибудь знает, как я мог это сделать? если вы считаете, что это невозможно сделать, и вы хотите предложить что-то, чтобы сделать графический интерфейс доступным во время выполнения процесса, я открыт для предложений.

Надеюсь, я был достаточно ясен. Заранее спасибо.


person Grego    schedule 31.03.2012    source источник
comment
Простой обходной путь для назначений свойств Enabled — переместить их в метод test_login_Click(). Но вам нужно будет включить их снова. Лучше всего использовать событие RunWorkerCompleted объекта BackgroundWorker. Разве я уже не упоминал об этом?   -  person Hans Passant    schedule 01.04.2012
comment
+1 В конечном счете, BackgroundWorker — это путь. Это хорошая оболочка вокруг голого потока, созданная именно для таких сценариев. Основная идея остается: вы не должны (и в большинстве случаев не можете) касаться пользовательского интерфейса из потока, отличного от пользовательского интерфейса. См. здесь пример того, как используй это.   -  person irobot    schedule 01.04.2012


Ответы (1)


Весь код, связанный с пользовательским интерфейсом, должен выполняться в потоке пользовательского интерфейса. В вашем случае это означает, что только код, который вы обозначили //..., должен выполняться в отдельном потоке. Извлеките этот долго выполняющийся код в отдельный метод и передайте этот метод ThreadStart вместо Login(). Затем вам нужно организовать способ, с помощью которого рабочий поток уведомляет поток пользовательского интерфейса, если и когда он будет завершен.

Обновлять:

Вот грубый пример того, как изменить ваш код. Я бы предпочел выделить длительную операцию в отдельный класс, если она имеет достаточную сложность, но я думаю, что вы поняли идею.

Вызов BeginInvoke гарантирует, что LongRunningOperationComplete будет выполняться в потоке пользовательского интерфейса формы. Вы можете использовать тот же подход для вызова других методов, которые обновляют пользовательский интерфейс, чтобы указать ход выполнения, даже если трудоемкая операция все еще выполняется. Если для этих методов требуется больше параметров, вы можете создать другие делегаты с соответствующей сигнатурой и передать эти параметры при вызове BeginInvoke. См. здесь, как это сделать.

// 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();
}
person irobot    schedule 31.03.2012
comment
дело в том, что часть с //... имеет код, который обновляет пользовательский интерфейс, поэтому, если я уберу эту часть в потоке, у меня будут те же проблемы, потому что ему придется обновлять элементы пользовательского интерфейса новыми информация, поступающая из //..., которые являются http-соединениями. - person Grego; 01.04.2012
comment
Вот почему я упомянул, что вам нужно организовать способ взаимодействия вашего рабочего потока с потоком пользовательского интерфейса. - person irobot; 01.04.2012
comment
BackgroundWorker был подходящим вариантом. :D Я провел эти дни, изучая это, и это правильно. :D - person Grego; 02.04.2012