c++ - 在托管 C++ GUI 中通过另一个事件结束/中断/切换一个事件

标签 c++ winforms events visual-c++ parallel-processing

我会尽量保持简单。假设我们创建了以下非常简单的 GUI。它包含两个按钮:

Image of my GUI 更新:我可以发布漂亮的图片! :)
我创建了两个静态变量:

public: static int counter = 0;  // our output
public: static int action = 0;   // info about action status

点击[START]按钮后,触发如下代码:

private: System::Void start_btn_Click(System::Object^  sender, System::EventArgs^  e) {

    // Change the action value so it is visible in the form
    action = 1;

    std::string str_value;
    String^ managed_str_value;

    for (int i = 0; i < 10000000; i++) {

        // Update the counter:
        counter = i;

        // I want to break the loop and print the value of 'counter'
        //in 'output_txt' textbox once I click the 'STOP' button
        if (action == 0) {

            // Conversion from int to managed string:
            std::ostringstream str_streamer;
            str_streamer << counter;
            str_value = str_streamer.str();
            managed_str_value = gcnew String(str_value.c_str());

            // Print in the textbox:
            this->output_txt->Text = managed_str_value;

            // Finish the loop:
            break;
        }
    }
}

然后,在点击按钮 [STOP] 后,我将 action 的值设置为 0,如下所示:

private: System::Void stop_btn_Click(System::Object^  sender, System::EventArgs^  e) {

    // On stop_btn click, I just switch action value to 0, which I expect 
    // to be noticed inside loop started by 'start_btn'
    action = 0;  
}

在一些用 LabVIEW 编写的项目中,我习惯了这样的事件处理,它工作得很好,但在这里,单击 [START] 按钮让我等待处理这个简单循环的结束,并卡住 GUI 的时间计算,因此,我没有机会在处理过程中停止它(这是必要的)。

另一个问题(我认为与此问题相关)是:为什么在将打印结果的代码移动到 output_txt(如下所示)后,我看不到文本框中更新的每个新值?

private: System::Void start_btn_Click(System::Object^  sender, System::EventArgs^  e) {

    // Change the action value so it is visible in the form
    action = 1;

    std::string str_value;
    String^ managed_str_value;

    for (int i = 0; i < 10000000; i++) {

        // Update the counter:
        counter = i;

        // Now I try to print the result every time I switch the counter:
        std::ostringstream str_streamer;
        str_streamer << counter;
        str_value = str_streamer.str();
        managed_str_value = gcnew String(str_value.c_str());

        this->output_txt->Text = managed_str_value;

        // I want to break the loop once I click the 'STOP' button
        if (action == 0) {

            // Finish the loop:
            break;
        }
    }
}

注意:整体问题要复杂得多,但这个例子尽可能简单,以保持案例的本质。任何帮助将不胜感激。

最佳答案

好的,经过一些研究后,我使用了 BackgroundWorker(在 Windows 窗体工具列表中可用)。现在它就像一个魅力。我发布了我对问题的解决方案,以便其他人可以从中受益。

BackgroundWorker 定义了三个事件:

    // Background Worker 
// What is being processed
private: System::Void backgroundWorker1_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e) {

        for (int i = counter; i < 1000000; i++) {

            // Sleep to prevent stack overflow (I think)
            System::Threading::Thread::Sleep(10);

            // Update the counter:
            counter = i;

            // Reporting progress:
            backgroundWorker1->ReportProgress(counter);

            // This happens when we trigger cancellation of work 
            // for Background Worker
            if (backgroundWorker1->CancellationPending) {
                 e->Cancel = true;
                 backgroundWorker1->ReportProgress(0);
                 return;
            }
        }
    // Final result
    e->Result = counter;
}

// How do we report progress
private: System::Void backgroundWorker1_ProgressChanged(System::Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e) {

        // Conversion from int to managed string:
        std::ostringstream str_streamer;
        str_streamer << e->ProgressPercentage;
        std::string str_value = str_streamer.str();
        String^ managed_str_value = gcnew String(str_value.c_str());

        // Display current status
        this->output_txt->Text = managed_str_value;
}

// What happens after work is done
private: System::Void backgroundWorker1_RunWorkerCompleted(System::Object^  sender, System::ComponentModel::RunWorkerCompletedEventArgs^  e) {
    if (e->Cancelled) {

        // Conversion from int to managed string
        std::ostringstream str_streamer;
        str_streamer << counter;
        std::string str_value = str_streamer.str();
        String^ managed_str_value = gcnew String(str_value.c_str());

        // Print current value
        this->output_txt->Text = managed_str_value;
    }
    else {
        this->output_txt->Text = e->Result->ToString();
    }
}

这是我单击两个按钮(开始/停止)时的操作:

// START button
private: System::Void start_btn_Click(System::Object^  sender, System::EventArgs^  e) {

        if (!backgroundWorker1->IsBusy) {

            // Start the execution asynchronously
            backgroundWorker1->RunWorkerAsync();
        }
        else {
            this->output_txt->Text = "Busy processing, please wait";
        }
}

// STOP button
private: System::Void stop_btn_Click(System::Object^  sender, System::EventArgs^  e) {

    if (backgroundWorker1->IsBusy) {

        // Cancel work in progress
        backgroundWorker1->CancelAsync();
    }
    else {
        this->output_txt->Text = "No operation in progress";
    }
}

希望这对其他人有帮助。如果有什么地方似乎没有优化(尤其是线程休眠),我将不胜感激您在阅读代码后想到的任何提示或评论。

关于c++ - 在托管 C++ GUI 中通过另一个事件结束/中断/切换一个事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29818838/

相关文章:

c# - 表单和代码不同步

c# - 如何创建代理以收听表单上的所有文本框

javascript - 你如何取消你的 href ="javascript:void(null)"?

java - 如果操作不是由用户触发的,如何禁用 SWT 事件监听

c++ - QGraphicsView 奇怪地呈现其子小部件的子小部件

c++ - 为什么 “memory_order_relaxed”在我的系统中被视为 “memory_order_seq_cst” [C++]

c++ - 光线追踪 - 反射

c++ - 如何在不使用任何数据库的情况下制作动态注册表单?

c# - 如何在 C# 中显示对象的数据?

c# - 选择在运行时保存文件的位置