I've got a WinForm running on my main thread and a while(true)
loop running on a separate thread. Each loop of that while(true)
creates a new System::String^
and I want to paste that String
into a TextBox
on my UI.
My file structure includes GUI.h
, GUI.cpp
, and Other.cpp
.
GUI.h
contains all the automatically created code for the main (and only) Form. It also has some Get
, Set
, and ButtonClick
methods.
//GUI.h
#pragma once
#include <string>
#include <vector>
#include <cliext\vector>
#include <conio.h>
#include <list>
#include <iostream>
extern void BufferRecieveLoop();
namespace GUI_Example_Receive {
static bool loopFlag = true;
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;
/// <summary>
/// Summary for GUI
/// </summary>
public ref class GUI : public System::Windows::Forms::Form
{
public:
GUI(void)
{
InitializeComponent();
}
std::vector<std::string> CollectText();
void ContinueNormally(); // Object^);
void DisableAllTextboxes();
void EnableAllTextboxes();
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~GUI()
{
if (components)
{
delete components;
}
}
private:
//Labels
//GroupBoxes
//Buttons
//SaveFile
public:
//TextBoxes
System::Windows::Forms::TextBox^ consoleTextBox;
private:
System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
void InitializeComponent(void)
{
//automatically made, lightly edited
}
#pragma endregion
public:
void SetConsoleTextBoxText(System::String^ input)
{
this->consoleTextBox->Text = input;
this->consoleTextBox->Refresh();
}
void ClearConsoleTextBoxText()
{
this->consoleTextBox->Clear();
}
delegate void MyDelegate(System::String ^ str);
void ClearAndSetConsoleTextBoxText(System::String ^ input)
{
/***************************************************
if (InvokeRequired)
{
this->BeginInvoke(gcnew MyDelegate(this, &ClearAndSetConsoleTextBoxText), { input });
}
***************************************************/
ClearConsoleTextBoxText();
SetConsoleTextBoxText(input);
}
System::Void startButton_Click(System::Object^ sender, System::EventArgs^ e)
{
loopFlag = true; //able to loop through ContinueNormally()
ContinueNormally(); //method in GUI.cpp
}
};
//https://social.msdn.microsoft.com/Forums/vstudio/en-US/4da834f0-d8f8-4abb-a655-ef9e99d51eb2/how-to-create-a-global-object-of-a-ref-class-type?forum=vcgeneral
ref struct Globals {
static GUI ^gui; //using Globals::gui everywhere to access the one Form
};
}
Gui.cpp
contains code to Run()
the form, start a thread, and loop forever.
//GUI.cpp
void BufferRecieveLoop()
{
while (true)
{
size_t bytes_read = multicast.Receive(buffer, Example::MTU_SIZE);
incoming.Process(buffer, bytes_read, endian); //method in Other.cpp
}
}
void GUI::ContinueNormally()
{
System::Threading::Thread ^loopThread = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(BufferRecieveLoop));
loopThread->Start();
loopThread->Join();
}
static void Start()
{
Globals::gui = gcnew GUI;
System::Windows::Forms::Application::Run(Globals::gui);
}
int __cdecl main(int argc, char* argv[])
{
System::Windows::Forms::Application::EnableVisualStyles();
System::Windows::Forms::Application::SetCompatibleTextRenderingDefault(false);
Start();
return 0;
}
Other.cpp
creates a String^
and calls a method within GUI.h
to change the text in a textbox.
//Other.cpp
void Process(const DIS::Pdu& packet)
{
System::String^ sysStr2 = "stuff";
GUI_Example_Receive::Globals::gui->ClearAndSetConsoleTextBoxText(sysStr2);
//GUI_Example_Receive::Globals::gui->BeginInvoke(gcnew MyStringDelegate(GUI_Example_Receive::Globals::gui, &GUI_Example_Receive::GUI::ClearAndSetConsoleTextBoxText), { sysStr2 });
}
I don't know where to properly Invoke
my methods. Nor do I know how to Invoke
my methods. A lot of what I've found is C# and hasn't worked for me.
Do I invoke from Other.cpp
or inside the method being called in GUI.h
?
In case anyone else has issues with this in the future and, like me, doesn't find many c++ code examples, I'll post my solution.
In my GUI.h
file, I've got a method to SetConsoleTextBoxText()
. This is usable only by the thread which owns consoleTextBox
. Therefore, any other thread which trys to call that method will need to Invoke()
the method (which is to relinquish control back to the owning thread).
//GUI.h
delegate void MyDelegate(System::String ^ text);
void SetConsoleTextBoxText(System::String^ input)
{
if (this->consoleTextBox->InvokeRequired) //is a thread other than the owner trying to access?
{
MyDelegate^ myD = gcnew MyDelegate(this, &GUI::SetConsoleTextBoxText);
//GUI is the ref class. Replace with wherever your function is located.
this->Invoke(myD, gcnew array<Object^> { input }); //Invoke the method recursively
}
else
{
//Normal function of this method. This will be hit after a recursive call or from the owning thread
this->consoleTextBox->Text = input;
this->consoleTextBox->Refresh();
}
}