C++ Visual Studio 2017 表格。如何从单独的线程更新和刷新标签

C++ Visual Studio 2017 Form. How can I update and refresh a label from a separate thread

我尝试过使用一些方法,但似乎无法将标签传递给新线程或让辅助线程能够访问标签并在表单线程上刷新表单。我试图让它成为多线程,这样当 运行 命令到远程设备时界面不会锁定。如果有任何帮助可以更新标签并从辅助线程刷新表单,我们将不胜感激!这是我的 MyForm.h 文件和 MyForm.cpp 文件:

#include <iostream>
#include <conio.h>
#include <windows.h>


namespace StudioControl2 {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::Threading;
    using namespace System::Threading::Tasks;
    using namespace std;

    bool GUIEvent = false;

    /// <summary>
    /// Summary for MyForm
    /// </summary>
    public ref class MyForm : public System::Windows::Forms::Form
    {
    public:
        MyForm (void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~MyForm()
        {
            if (components)
            {
                delete components;
            }
        }

    private: System::Windows::Forms::Button^  WindowTVVmixSelect;
    private: System::Windows::Forms::Label^  StatusOutput;


    private: System::ComponentModel::IContainer^  components;
    protected:

    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>


#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            System::ComponentModel::ComponentResourceManager^  resources = (gcnew System::ComponentModel::ComponentResourceManager(MyForm::typeid));
            this->WindowTVVmixSelect = (gcnew System::Windows::Forms::Button());
            this->StatusOutput = (gcnew System::Windows::Forms::Label());
            (cli::safe_cast<System::ComponentModel::ISupportInitialize^>(this->pictureBox1))->BeginInit();
            this->SuspendLayout();
            // 
            // WindowTVVmixSelect
            // 
            this->WindowTVVmixSelect->Anchor = System::Windows::Forms::AnchorStyles::None;
            this->WindowTVVmixSelect->Location = System::Drawing::Point(31, 400);
            this->WindowTVVmixSelect->Margin = System::Windows::Forms::Padding(5);
            this->WindowTVVmixSelect->Name = L"WindowTVVmixSelect";
            this->WindowTVVmixSelect->Size = System::Drawing::Size(120, 30);
            this->WindowTVVmixSelect->TabIndex = 10;
            this->WindowTVVmixSelect->Text = L"Vmix";
            this->WindowTVVmixSelect->UseVisualStyleBackColor = true;
            this->WindowTVVmixSelect->Click += gcnew System::EventHandler(this, &MyForm::WindowTVVmixSelect_Click);
            // 
            // StatusOutput
            // 
            this->StatusOutput->Anchor = System::Windows::Forms::AnchorStyles::None;
            this->StatusOutput->BorderStyle = System::Windows::Forms::BorderStyle::Fixed3D;
            this->StatusOutput->ForeColor = System::Drawing::Color::Red;
            this->StatusOutput->Location = System::Drawing::Point(232, 612);
            this->StatusOutput->Margin = System::Windows::Forms::Padding(5);
            this->StatusOutput->Name = L"StatusOutput";
            this->StatusOutput->Size = System::Drawing::Size(120, 30);
            this->StatusOutput->TabIndex = 20;
            this->StatusOutput->TextAlign = System::Drawing::ContentAlignment::MiddleCenter;
            // 
            // MyForm
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(584, 661);
            this->Controls->Add(this->WindowTVVmixSelect);
            this->MinimumSize = System::Drawing::Size(600, 700);
            this->Name = L"MyForm";
            this->Text = L"Studio Control";
            (cli::safe_cast<System::ComponentModel::ISupportInitialize^>(this->pictureBox1))->EndInit();
            this->ResumeLayout(false);
            this->PerformLayout();

        }
#pragma endregion

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

    if (GUIEvent == false) {
    GUIEvent = true;
    Console::WriteLine("Window TV Vmix Select");
    WindowHDMIStatus -> Text = "Vmix";
    StatusOutput->Text = "Please Wait";
    MyForm::Refresh();
//Creation of new thread.
    ThreadWindowTVVmixSelect^ o1 = gcnew ThreadWindowTVVmixSelect();
    Thread^ t1 = gcnew Thread(gcnew ThreadStart(o1, &ThreadWindowTVVmixSelect::ThreadEntryPoint));
    t1->Name = "t1";
    t1->Start();
    }
}


        ref class ThreadWindowTVVmixSelect
        {
        public:
            void ThreadEntryPoint()
            {
                Sleep(2000);
                Console::WriteLine("Terminating Thread Window TV Vmix Select");

                //Trying to update and refresh label in MyForm.

                GUIEvent = false;
            }
        };
};
}
#include "MyForm.h"

using namespace System;
using namespace System::Windows::Forms;

[STAThreadAttribute]
int main(array<String^>^ args) {
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false);
    StudioControl2::MyForm form;
    Application::Run(%form);
    return 0;
}

如图所示MS Docs Forms Invoker

您需要创建所谓的调用程序来调用您在创建他的线程中给他的命令,因此它是委托。

编辑:

没想到是cpp,语法有点不同,但主要思想是一样的 Define and ise delegates MS Doc

我想出了如何从我提到的同一站点 (https://docs.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-make-thread-safe-calls-to-windows-forms-controls) 进行线程安全调用。页面右上角有一个小符号 (</>),可将代码示例更改为三个选项之一:C#、VB 或 C++。

这是我用于设置新线程的代码,以及 运行 从新线程对 UI 线程上的表单标签进行线程安全调用:

private:
    Thread^ Thread1;
private: System::Void WindowTVVmixSelect_Click(System::Object^  sender, System::EventArgs^  e) {

    if (GUIEvent == false) {
    GUIEvent = true;
    Console::WriteLine("Window TV Vmix Select");
    StatusOutput->Text = "Please Wait";
    MyForm::Refresh();
    //Creation of new thread.
    this->Thread1 =
        gcnew Thread(gcnew ThreadStart(this, &MyForm::ThreadWindowTVVmixSelect));
    this->Thread1->Start();
    }
}
private:
        void ThreadWindowTVVmixSelect()
        {
            Sleep(3000);
            //SetTextWindowHDMIStatus("Vmix");
            SetTextStatusOutput("");
            GUIEvent = false;
        }
    delegate void StringArgReturningVoidDelegate(String^ text);
             //Sets the status label in the form
             private:
                 void SetTextStatusOutput(String^ text)
                 {
                     // InvokeRequired required compares the thread ID of the
                     // calling thread to the thread ID of the creating thread.
                     // If these threads are different, it returns true.
                     if (this->StatusOutput->InvokeRequired)
                     {
                         StringArgReturningVoidDelegate^ d =
                             gcnew StringArgReturningVoidDelegate(this, &MyForm::SetTextStatusOutput);
                         this->Invoke(d, gcnew array<Object^> { text });
                     }
                     else
                     {
                         this->StatusOutput->Text = text;
                     }
                 }

希望这对希望从另一个线程更新 C++ CLR 表单的其他人有所帮助。