简体   繁体   中英

Wait for thread to finish without blocking UI thread

I am writing winForm app which have to listem SerialPort and print the data into multi line textbox in real time.

Here is the code:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.ComponentModel;

namespace SCPListener
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
            GetAvailablePortNames();
            panel1.BackColor = ColorTranslator.FromHtml("#f44147");
            cbDefaultValue();
        }

        String[] PortNames;
        bool conn = true;
        String data;
        public string datetime;
        Form2 frm = new Form2();

        void GetAvailablePortNames()
        {
            PortNames = SerialPort.GetPortNames();
            comboBox1.Items.AddRange(PortNames);
        }


        void RefreshAvailablePortNames()
        {
            comboBox1.Items.Clear();
            PortNames = SerialPort.GetPortNames();
            comboBox1.Items.AddRange(PortNames);
        }
        public string getDataTime()
        {
            DateTime time = DateTime.Now;
            string date = time.ToString(@"hh\:mm\:ss");
            return date;
        }
        public void GetData()
        {
            string date = getDataTime();
            data = serialPort1.ReadLine();
            textBox1.AppendText("[" + date + "] " + "Received: " + data + "\r\n");
        }
        public void cbDefaultValue()
        {
            comboBox1.SelectedIndex = 0;
            comboBox5.SelectedIndex = 0;
            comboBox2.SelectedIndex = 0;
            comboBox3.SelectedIndex = 0;
            comboBox4.SelectedIndex = 0;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (comboBox1.Text == "" || comboBox2.Text == "" || comboBox3.Text == "" || comboBox4.Text == "" || comboBox5.Text == "")
                {
                    MessageBox.Show("Please port settings", "Incorrect port settings", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    DateTime time = DateTime.Now;
                    string date = time.ToString(@"hh\:mm\:ss");
                    serialPort1.PortName = comboBox1.Text;
                    serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
                    switch (comboBox3.Text)
                    {
                        case "5":
                            serialPort1.DataBits = 5;
                            break;
                        case "6":
                            serialPort1.DataBits = 6;
                            break;
                        case "7":
                            serialPort1.DataBits = 7;
                            break;
                        case "8":
                            serialPort1.DataBits = 8;
                            break;
                        default:
                            serialPort1.DataBits = 5;
                            break;
                    }
                    switch (comboBox4.Text)
                    {
                        case "None":
                            serialPort1.Parity = Parity.None;
                            break;
                        case "Odd":
                            serialPort1.Parity = Parity.Odd;
                            break;
                        case "Even":
                            serialPort1.Parity = Parity.Even;
                            break;
                        case "Mark":
                            serialPort1.Parity = Parity.Mark;
                            break;
                        case "Space":
                            serialPort1.Parity = Parity.Space;
                            break;
                    }
                    switch (comboBox5.Text)
                    {
                        case "One":
                            serialPort1.StopBits = StopBits.One;
                            break;
                        case "Two":
                            serialPort1.StopBits = StopBits.Two;
                            break;
                        case "OnePointFive":
                            serialPort1.StopBits = StopBits.OnePointFive;
                            break;
                    }
                    serialPort1.Open();
                    string CurrentPornName = comboBox1.Text;
                    label7.Text = "Opened " + CurrentPornName;
                    panel1.BackColor = ColorTranslator.FromHtml("#42f477");
                    comboBox1.Enabled = false;
                    comboBox2.Enabled = false;
                    comboBox3.Enabled = false;
                    comboBox4.Enabled = false;
                    comboBox5.Enabled = false;
                    //button5.Enabled = false;
                    //button6.Enabled = false;
                }
            }
            catch (Exception ex)
            {
                if (ex is UnauthorizedAccessException)
                {
                    MessageBox.Show("Unauthorized Acces","Access Error",MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else if (ex is System.IO.IOException)
                {
                    MessageBox.Show("Please plug in your device", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            panel1.BackColor = ColorTranslator.FromHtml("#f44147");
            string CurrentPornName = comboBox1.Text;
            label7.Text = "Closed " + CurrentPornName; ;
            serialPort1.Close();
            comboBox1.Enabled = true;
            comboBox2.Enabled = true;
            comboBox3.Enabled = true;
            comboBox4.Enabled = true;
            comboBox5.Enabled = true;
            //button5.Enabled = true;
            //button6.Enabled = true;
        }

        private void button5_Click(object sender, EventArgs e)
        {
            RefreshAvailablePortNames();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //backgroundWorker1.RunWorkerAsync();
            Thread thread = new Thread(start: ()=>
            {
                try
                {
                    datetime = getDataTime();
                    string date = getDataTime();
                    startListening:
                    if (serialPort1.BytesToRead > 0)
                    {
                        datetime = getDataTime();
                        data = serialPort1.ReadLine();
                        textBox2.AppendText("[" + datetime + "] " + "Received: " + data + "\r\n");
                    }
                    else
                        textBox2.AppendText("[" + datetime + "] " + "Error: There is no data to read" + "\r\n");
                    if (conn)
                        goto startListening;
                }

                catch (Exception ex)
                {

                    DateTime time = DateTime.Now;
                    string date = time.ToString(@"hh\:mm\:ss");
                    if (ex is TimeoutException)
                    {
                        textBox1.AppendText("[" + date + "] " + "Timuot Exception" + "\r\n");
                    }
                    else if (ex is InvalidOperationException)
                    {
                        MessageBox.Show("Plese check settings", "Error: Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else if (ex is System.IO.IOException)
                    {
                        MessageBox.Show("Port opened, but device unpluged..", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                }
            });
            thread.Start();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            conn = false;
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
                try
                {
                    datetime = getDataTime();
                    string date = getDataTime();
                    startListening:
                    if (serialPort1.BytesToRead > 0)
                    {
                        datetime = getDataTime();
                        data = serialPort1.ReadLine();
                        textBox2.AppendText("[" + datetime + "] " + "Received: " + data + "\r\n");
                    }
                    else
                        textBox2.AppendText("[" + datetime + "] " + "Error: There is no data to read" + "\r\n");
                    if (conn)
                        goto startListening;
                }

                catch (Exception ex)
                {

                    DateTime time = DateTime.Now;
                    string date = time.ToString(@"hh\:mm\:ss");
                    if (ex is TimeoutException)
                    {
                        textBox1.AppendText("[" + date + "] " + "Timuot Exception" + "\r\n");
                    }
                    else if (ex is InvalidOperationException)
                    {
                        MessageBox.Show("Plese check settings", "Error: Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else if (ex is System.IO.IOException)
                    {
                        MessageBox.Show("Port opened, but device unpluged..", "IO Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }

                }

        }
    }
}

About Code:

comboBox1 -> where the Ports are listed
comboBox2 -> where the Baud Rates options are listed
comboBox3 -> where the Stop Bits options are listed
comboBox4 -> where the Data Bits options are listed
comboBox5 -> where the Parity options are listed
button1 -> to connect chosen port
button2 -> to disconnect port
button3 -> to start listening and print data into textBox in real time
button4 -> to stop listening
button5 -> refresh available ports
panel1-> to identify port is connected or not (if connected it is green if it is not connected it is red)

So, what i want to do is that when I click the stop button ( button4 ), program have to stop listening. I am trying to achieve this result using backgroundWorker . Using this code when i try to listen port (clicking start button button3 there is opening the small error window and tell please check settings (so it catching InvalidOperationException according to my code); If I wont use backgoundWorker and also remove try{...} cath{...} program start do display the received data from port but blocking the UI. Any ideas will be very helpful.

button4_click needs to call backgroundWorker1.CancelAsync() instead of setting a bool. With BackgroundWorkers, you need to specify that you want them to end and then check for it, setting and checking an outside bool will not work. The try block in your backgroundWorker1_DoWork() method should look like the following. Keep in mind, it is easier for you and others to help you if you rename your variables (either done in the UI or by right clicking the variable and selecting 'Rename'). Another note before the code, it is almost never ok to use 'goto', there is almost always a better option (a while loop in this case).

datetime = getDataTime();
string date = getDataTime();
while (!backgroundWorker1.CancellationPending)
{
    if (serialPort1.BytesToRead > 0)
    {
        datetime = getDataTime();
        data = serialPort1.ReadLine();
        textBox2.AppendText($"[{datetime}] Received: {data}\r\n");
    }
    else
    {
        textBox2.AppendText($"[{datetime}] Error: There is no data to read\r\n");
    }
}

Edit to say: This should get you started, you will also want to check out this link to access your UI from backgroundWorker1, the problem is your UI is on one thread and your BackgroundWorker on another, threads do not work well together without some complimenting code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM