Как реализовать текстбокс, зависящий от изменяющейся переменной
Необходимо в текстбокс транслировать информацию, приходящую с COM порта.
Пока все работает так: открывается порт и запускается функция ReadCOM(), бесконечно сканирующая порт, и каждый приходящий символ ею возвращается и закидывается в строку sRecevedChar. Далее по нажатию кнопки вызывается Read_button_Click и тогда должен запускаться новый поток, как я полагаю, который будет непрерывно это все обрабатывать?
Но как мне правильно реализовать этот поток?
std::string COMMaster()
{
if (sPortIsOpen == false) { _tOpenCOM(); sPortIsOpen = true; };
string sRecevedChar = ReadCOM();
return sRecevedChar;
}
System::Void MKDiag::MainForm::Read_button_Click(System::Object^ sender, System::EventArgs^ e)
{
MKDiag::MainForm::ReadtextBox->Text += Convert_string_to_String(COMMaster(), ReadtextBox->Text);
}
Было бы разумно реализовать функцию в обработчике sPortIsOpen, куда все это запихнуть. После - запустить эту функцию, Thread-ом, но то ли нельзя функцию в методах создавать, то ли руки кривые.
ps и код конвертера, на всякий случай:
//конвертируем std::string в System::string^
String^ Convert_string_to_String(std::string& os, String^ s) {
s = gcnew System::String(os.c_str());
return s;
}
Ответы (2 шт):
Как-то у вас всё сложно, читать из COM-порта можно и на C#.
Заведите себе простейшее приложение на WPF, в окне положите
<TextBox TextWrapping="Wrap" VerticalAlignment="Stretch" Name="TextContainer"/>
а в code-behind (MainWindow.xaml.cs)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
GoRead();
GoUpdate();
}
StringBuilder cumulatedText = new StringBuilder();
async void GoRead()
{
var com = new SerialPort(portName: "COM1", baudRate: 19200, parity: Parity.None,
dataBits: 8, stopBits: StopBits.One);
com.Open();
var stream = com.BaseStream;
byte[] buf = new byte[32768];
while (true)
{
var actuallyRead = await stream.ReadAsync(buf, 0, buf.Length);
// так просто можно только с однобайтными кодировками наподобие Encoding.ASCII
var s = Encoding.ASCII.GetString(buf, 0, actuallyRead);
cumulatedText.Append(s);
}
}
async void GoUpdate()
{
while (true)
{
await Task.Delay(200);
TextContainer.Text = cumulatedText.ToString();
}
}
}
Вам придётся, возможно, установить nuget-пакет System.IO.Ports.
Запуск GoRead и GoUpdate можно перенести в OnClick, если хотите.
По идее, должно работать.
Основная сложность тут не в том, чтобы запустить вашу функцию в отдельном потоке, а в том, как реализовать взаимодействие фонового потока с UI, учитывая что Winforms - однопоточный фреймворк. Для этого можно использовать BeginInvoke, но на этом проблемы не заканчиваются, так как BeginInvoke нельзя вызывать после закрытия окна. Попытка просто проверить свойство IsDisposed наталкивается на состояние гонки, ведь окно может быть закрыто одновременно с проверкой. Итоговый код с учетом этого будет выглядеть так (я слегка упростил функцию Convert_string_to_String):
public ref class MyForm : public System::Windows::Forms::Form
{
public:
System::Threading::Thread^ thread;
bool exit;
System::Object^ sync;
MyForm(void)
{
InitializeComponent();
thread=nullptr;
exit=false;
sync = gcnew System::Object();
}
std::string COMMaster(){
//...
}
String^ Convert_string_to_String(std::string& os) {
return gcnew System::String(os.c_str());
}
void SetTexbox(System::String^ val){
textBox1->Text=val; //установка значения TextBox
}
void ThreadProc(){
auto deleg=gcnew System::Action<System::String^>(this,&MyForm::SetTexbox);
while(1){
//читаем строку
auto str=COMMaster();
System::String^ str_converted=Convert_string_to_String(str);
System::Threading::Monitor::Enter(sync);
try{
if(exit)break; //поток остановлен
this->BeginInvoke(deleg,str_converted); //устанавливаем значения TextBox
}
finally{
System::Threading::Monitor::Exit(sync);
}
}
}
System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
if(thread!=nullptr)return;
//запускаем поток
auto x=gcnew System::Threading::ThreadStart(this,&MyForm::ThreadProc);
thread = gcnew System::Threading::Thread(x);
thread->Start();
}
System::Void MyForm_FormClosed(System::Object^ sender, System::Windows::Forms::FormClosedEventArgs^ e) {
//при закрытии формы останавливаем поток
System::Threading::Monitor::Enter(sync);
try{
exit=true;
}
finally{
System::Threading::Monitor::Exit(sync);
}
}
}