Search code examples
c#user-interfaceserial-portthread-synchronization

Threading and updating the GUI


I've been working on a WinForms utility that takes commands and/or data from a text file and based on the contents of this file transmits or requests further data over serial. This all works perfectly but I am struggling with working out how to get GUI to give some feedback that something is happening. In its current structure the main form loads and the Load() Event opens the text file and processes its contents

tp = new TProcess();
tp.FileOpen();

TProcess and all the other classes involved in communicating over the serial port are collected into a sub-project that compiles as a DLL. In turn TProcess will create a new instance of my SerialDevice object (also defined in my DLL) and pass it my of data then tell it what to do with it e.g.

//TProcess


if (File.Exists(filey))
            try
                {

                StreamReader file = new StreamReader(@"filey");



                List<clsStuff> stuffList = new List<clsStuff>();

                while ((line = file.ReadLine()) != null)
                    {
                    //process and add to list
                    }

                    var SD = new SerialDevice();
                    SD.List = stuffList;                        
                    SD.Send();

                file.Close();

                }
            catch (Exception e)
                {
              //Write Error File

                }

        }

My SD Class defines all the port parameters, sends the first item packets and receives an acknowledge packet from the remote device using the DataReceived Event, it then sends the items from the list one at a time until it has finished and Exits. My question therefore (sorry long winded) is how can I pass some kind feedback (perhaps a string like Record X recieved each time I get a packet acknowledgement back) that things are happening to the GUI given that the GUI thread did not create my SD instance, if this were a more compact program and the GUI thread had called SD I would use a delegate and BeginInvoke but I don't see how I can do that here as the GUI knows nothing about the SD instance and likewise SD knows nothing about the GUI. I feel this might be solved by SynchronisationContext but I have no idea how to implement it.

         public void Send()
                {
                if (!serialP.IsOpen)
                    {
                    PortOpen();
                    serialP.ReceivedBytesThreshold = 4;
                    }
                serialP.Write(CurrentListItem, 0, 11);


                }
      private void serialP_DataEvent(object sender, SerialDataReceivedEventArgs e)// This triggers when the response is received
                {


                        byte[] Ack = new byte[4];
                        serialP.Read(Ack, 0, 4);
                        //snip of course I check these 4 bytes etc here
                                CurrentListItem++;
                                //UPDATE THE GUI BUT HOW?
                                Send()!;//next
                  }

Solution

  • To your TProcess class add an event that your UI can subscribe to:

    public event EventHandler DataRecieved;
    

    Then in your serialP_DataEvent you can, at the appropriate point do:

    if (DataRecieved != null)
    {
        DataRecieved(this,EventArgs.Empty);
    }
    

    Your UI can then subscribe to this:

    tp.DataRecieved += (o,e) => { // do something to the UI };
    

    A couple of notes:

    1) You can pass information in the event if you use the generic EventHandler<T> instead of EventHandler. Where T is the class of whatever data you want to send

    2) It looks like FileOpen is synchronous and blocking so it will stop your UI from updating. Make it asynchronous or at least use Task.Run

    3) If you make it asynchronous, you may then have the problem of trying to update the UI from a thread that isn't the UI thread. You will need to use Invoke to push the UI update back to the UI thread (you don't say if this is WinForms or WPF, but they are similar).