Search code examples
c#arraylistc#-4.0telnet

Logic issue on reading ALL data from the from the listbox that is coming from thread in c#


I am modifying to first question attempt.

I need help passing the data from a listbox and pass it to another method so every time the listbox gets data add it from the tread, it should also send that data to my new method and it to the my list because in that method I will be doing some parsing because the data from the listbox is a long string barcode but I don't need help on parsing the data because I can do that, the part I need help only is to pass the data from thread that is passing it to the listbox it should also be send to my method ReadAllData() so in that method I will received the data and then I will do the parsing the make a return.

Here is the method where the listbox is stored and receives the data from a thread from telnet port 23

Here is the code I need to send the data from the lst_BarcodeScan to the method ReadAllData method and store to my list.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.Remoting.Messaging;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace BarcodeReceivingApp
{
    public class TelnetConnection
    {
        private Thread _readWriteThread;
        private TcpClient _client;
        private NetworkStream _networkStream;
        private string _hostname;
        private int _port;
        private BarcodeReceivingForm _form;
        private bool _isExiting = false;

        public TelnetConnection(string hostname, int port)
        {
            this._hostname = hostname;
            this._port = port;
        }

        public TelnetConnection()
        {

        }

        public void ServerSocket(string ip, int port, BarcodeReceivingForm f)
        {

            this._form = f;
            try
            {
                _client = new TcpClient(ip, port);
            }
            catch (SocketException)
            {
                MessageBox.Show(@"Failed to connect to server");
                return;
            }


            _networkStream = _client.GetStream();
            _readWriteThread = new Thread(ReadWrite);
            //_readWriteThread = new Thread(() => ReadWrite(f));
            _readWriteThread.Start();
        }


        public void Exit()
        {
            _isExiting = true;
        }

        public void ReadWrite()
        {

            var received = "";
            do
            {
                received = Read();
                if (received == null)
                    break;

                if (_form.lst_BarcodeScan.InvokeRequired)
                {
                    _form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
                    {
                        _form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
                    }));
                }    

            } while (!_isExiting);



            CloseConnection();


        }

    public List<string> ReadAllData()
    {
        var obtainData = new List<string>();



       return obtainData;
    }

        public string Read()
        {
            var data = new byte[1024];
            var received = "";

            var size = _networkStream.Read(data, 0, data.Length);
            if (size == 0)
                return null;

            received = Encoding.ASCII.GetString(data, 0, size);

            return received;
        }

        public void CloseConnection()
        {
            MessageBox.Show(@"Closed Connection",@"Important Message");
            _networkStream.Close();
            _client.Close();
        }
    }
}

Main class that will call the methods from the telnetconnection class or any other classes I will add.

using System;
using System.Windows.Forms;

namespace BarcodeReceivingApp
{
    public partial class BarcodeReceivingForm : Form
    {
        //GLOBAL VARIABLES
        private const string Hostname = "myip";
        private const int Port = 23;
        private TelnetConnection _connection;


        public BarcodeReceivingForm()
        {
            InitializeComponent();

            //FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            WindowState = FormWindowState.Maximized;
        }

        private void btn_ConnectT_Click(object sender, EventArgs e)
        {
            _connection = new TelnetConnection(Hostname, Port);
            _connection.ServerSocket(Hostname, Port, this);

        }

        private void btn_StopConnection_Click(object sender, EventArgs e)
        {
            //_connection = new TelnetConnection(Hostname, Port);
            //_connection.ServerSocket(Hostname, Port, this);
            _connection.Exit();
        }

        private void btn_RemoveItemFromListAt_Click(object sender, EventArgs e)
        {
            for (var i = lst_BarcodeScan.SelectedIndices.Count - 1; i >= 0; i--)
            {
                lst_BarcodeScan.Items.RemoveAt(lst_BarcodeScan.SelectedIndices[i]);
            }
        }

        private void BarcodeReceivingForm_Load(object sender, EventArgs e)
        {
            lst_BarcodeScan.SelectionMode = SelectionMode.MultiSimple;
        }

        private void btn_ApplicationSettings_Click(object sender, EventArgs e)
        {
            var bcSettingsForm = new BarcodeReceivingSettingsForm();
            bcSettingsForm.Show();
        }

        private void btn_ClearBarcodeList_Click(object sender, EventArgs e)
        {
            lst_BarcodeScan.Items.Clear();
        }
    }
}

Solution

  • The easiest way to implement without adding more complexity to the thread is to simply raise an event on the listbox when and item is added to it. The problem is that the listbox do no have any events that allow to do that but you can create your own version with a dozen lines of code.

    The goal is to create a derived control of the listbox then you add to it a method to trigger a custom event when an item is added and bingo.

    Here's the custom listbox class with the custom EventArgs.

    // custom override class over the list box so we can create an event when items are added
    public class ListBoxWithEvents : ListBox
    {
        // the event you need to bind to know when items are added
        public event EventHandler<ListBoxItemEventArgs> ItemAdded;
    
        // method to call to add items instead of lst.Items.Add(x);
        public void AddItem(object data)
        {
            // add the item normally to the internal list
            var index = Items.Add(data);
    
            // invoke the event to notify the binded handlers
            InvokeItemAdded(index);
        }
    
        public void InvokeItemAdded(int index)
        {
            // invoke the event if binded anywhere
            ItemAdded?.Invoke(this, new ListBoxItemEventArgs(index));
        }
    }
    
    // basic event handler that will hold the index of the item added
    public class ListBoxItemEventArgs : EventArgs
    {
        public int Index { get; set; } = -1;
        public ListBoxItemEventArgs(int index)
        {
            Index = index;
        }       
    }
    

    Now you need to change your listbox on your form with the ListBoxWithEvents instead. You have 2 ways to do this but i'll give you the easiest. Compile your code and go in the design window for the form. In your toolbox you should have the ListBoxWithEvents control now and you can simply drag and drop in your form and replace the one you have. Since it derive from the listbox all it has is extra features. It didn't lose anything it had before.

    Now you need to change 2 things. In your form select the new ListBoxWithEvents control and go in the properties events and you will find the new event called ItemAdded you can double click that and it should create an event like the following. I have also thrown in a sample MessageBox that display all you will need.

    private void ListBox1_ItemAdded(object sender, ListBoxItemEventArgs e)
    {
        MessageBox.Show("Item was added at index " + e.Index + " and the value is " + listBox1.Items[e.Index].ToString());
    }
    

    Finally in order to trigger that event you need to use the new method lst.AddItem(object); instead of lst.Items.Add(object); so according to your sample code you need to change this :

    _form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
    {
        _form.lst_BarcodeScan.Items.Add(received + Environment.NewLine);
    }));
    

    to this :

    _form.lst_BarcodeScan.Invoke(new MethodInvoker(delegate
    {
        _form.lst_BarcodeScan.AddItem(received + Environment.NewLine);
    }));
    

    Try it and now you should have that event fire every time something is added to the list.

    Since you are pretty new to programming i find it important to mention that this event will trigger on the UI thread and not the thread you created. This mean it behave normally like clicking on a button triggers a button_click(object sender, EventArgs e) event. No special thread involved whatsoever.