使用 C++/CLR Windows 表单更新 GUI

Update GUI using C++/CLR Windows Forms

您好,我正在尝试创建一个可以读取数据、处理数据并将其实时绘制在图表上的应用程序,直到我按下 ESC 停止应用程序读取和处理更多数据。我在 Visual C++/CLR 中创建了这个应用程序:
My Real Time Data Processing and Plotting App

当我点击分析数据按钮时(见图)。正在读取和处理数据(基于控制台 window 中的结果),但我的 GUI 中的图形不会实时更新。它只会更新,直到我停止应用程序读取和处理更多数据。我知道这个问题与线程有关。我相信我 运行 在更新 GUI 的同一线程上读取和处理数据。下面是我进行数据抓取和处理以及 GUI 更新的代码:

private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
    vector<int> test;
    test.push_back(1);
    PD.setChannels(test);

    //Grab and process data until the escape key is pressed
    while (!GetAsyncKeyState(VK_ESCAPE)) {
        PD.refreshBufferSize();
        PD.grabDataFromBuffer();
        vector<double> fireRates = PD.getFireRates();
        //Debug code to print to console
        for (int i = 0; i < fireRates.size(); i++) {
            cout << fireRates[i] << endl;
        }
        Sleep(10); //wait 10 ms before getting the next buffer of data

        //update GUI below
        vector<int> channels = PD.getChannels();
        for (int i = 0; i < channels.size(); i++) {
            chart1->Series["Fire Rate"]->Points->AddXY(channels[i], fireRates[i]);
        }           
    }
}

我尝试寻找解决方案并找到了使用 BeginInvoke、delegate 和 Invoke 方法的内容,但它们中的大多数都在 C# 中并且很难理解。我使用 C++ 的原因是因为我使用一些 DLL 文件来调用某些函数,这些函数可以帮助我获取我想要处理和分析的数据,而根据我的经验,C# 在尝试使用 DLL 文件时发挥了很大作用。此外,我找到的关于如何使用我提到的 C++ 中的方法的文档和示例不是很清楚,或者不能很好地转换为 C++。

我的问题是如何在 C++/CLR 中实时更新我的​​ GUI 图表?是否可以使用 C++/CLR 这样做?如果可以,你能举个例子吗?非常感谢您的帮助!谢谢!

你是对的 - GUI 线程被限制在 while 循环中执行并且因为它永远不会离开方法,它永远不会执行 window 消息处理,包括 WM_PAINT消息,因此屏幕永远不会重绘。

您需要将获取放在单独的线程上,然后将调用同步回 GUI 以进行更新。这是一个例子:

private: System::Void button2_Click(System::Object^  sender, System::EventArgs^  e) {
    System::Threading::ThreadPool::QueueUserWorkItem(gcnew WaitCallback(acquireData));
}

void acquireData(Object^ state)
{    
    vector<int> test;
    test.push_back(1);
    PD.setChannels(test);

    //Grab and process data until the escape key is pressed
    while (!GetAsyncKeyState(VK_ESCAPE)) {
        PD.refreshBufferSize();
        PD.grabDataFromBuffer();
        vector<double> fireRates = PD.getFireRates();
        //Debug code to print to console
        for (int i = 0; i < fireRates.size(); i++) {
            cout << fireRates[i] << endl;
        }
        Sleep(10); //wait 10 ms before getting the next buffer of data

        //update GUI below
        vector<int> channels = PD.getChannels();
        for (int i = 0; i < channels.size(); i++) {
            chart1->BeginInvoke(gcnew Action<int, int>(updateChart), gcnew array<Object^>(channels[i], fireRates[i]));
        }
    }
}

void updateChart(int channel, int fireRate)
{
    chart1->Series["Fire Rate"]->Points->AddXY(channel, fireRate);
}

一个问题是它在一个紧密的循环中调用 BeginInvoke。这可能会使性能受到影响(一定要进行测量以查看是否确实如此)。这是一个问题的原因是您正在使用 vector<int> 并且您不能将此类型传递给任何托管方法。您需要将数据从向量中复制到 .Net 类型中,以便将其传递到 BeginInvoke.