Ando
Ando

Reputation: 121

Correct way to update a GUI from a class in C#

I am looking for a way to update a GUI from a class that I want to be stand alone and not rely on the GUI. It is a network class consisting of both a listener and a client. This class can connect and then send/receive data. I would like this data to be able to be displayed using a GUI but not have any of the GUI related code within the class itself.

So in short the network class only knows of itself. The GUI knows of the network class.

This is where I would like to add the code

    public void ReceiveBytes()
    {
        byte[] receivedPacket;

        while (IsListeningForBytes)
        {
            receivedPacket = new byte[Packet.BUFFERSIZE];

            try
            {
                int bytesRead = ClientSocket.Receive(receivedPacket, SocketFlags.None);

                if (bytesRead > 0)
                {
                    SocketObject.ProcessMessage(receivedPacket);
                    // Update GUI here after the message has been processed.
                }
                else
                {
                    throw new Exception("No bytes read");
                }
            }
            catch (Exception ex)
            {
                IsListeningForBytes = false;
                Disconnect();
                Console.WriteLine(ex.Message);
            }
        }
    }

Edit: Sorry everyone I will try and make things clearer. I am using windows forms and for the sake of this exercise we'll say I have three different controls: a listbox, a combobox and a textbox, that will take the data dependant of what is sent. (My actual application has listboxes, combo boxes, checkboxes ect that will need to be updated).

I understand that I shouldn't reference the GUI from within my object, hence my question.

As for relevant code, I'm not sure what you would like to see.

I have read about eventhandlers and delegates but I'm unsure how to actually implement it in this case. I originally passed a GUI update method as an action to the class that was called when required but this seemed long winded and didn't specifically update the control that I wanted.

Thanks for your help so far.

Upvotes: 0

Views: 407

Answers (1)

Stefan
Stefan

Reputation: 17648

It would be a violation of OOP to directly update your UI from your object.

Instead, implement an event to let the caller/user know something has happened, and make it their responsibility to update the UI.

Here's an example:

//added event, SomeDataObject is for you to create.
public event EventHandler<SomeDataObject> MessageProcessed;
public void ReceiveBytes()
{
    byte[] receivedPacket;

    while (IsListeningForBytes)
    {
        receivedPacket = new byte[Packet.BUFFERSIZE];

        try
        {
            int bytesRead = ClientSocket.Receive(receivedPacket, SocketFlags.None);

            if (bytesRead > 0)
            {
                SocketObject.ProcessMessage(receivedPacket);
                // no UI update but fire an event
                MessageProcessed?.Invoke(this, new SomeDataObject());
            }
            else
            {
                throw new Exception("No bytes read");
            }
        }
        catch (Exception ex)
        {
            IsListeningForBytes = false;
            Disconnect();
            Console.WriteLine(ex.Message);
        }
    }
}

See:

Understanding events and event handlers in C#

or

https://msdn.microsoft.com/en-us/library/db0etb8x(v=vs.110).aspx

or

https://duckduckgo.com/?q=EventHandler+C%23&t=h_&ia=qa

update


So, how it works:

In you FooGUI class, you'll need to subscribe the event.

//so, your UI, can be a window, a form or ... console.
//I'll call your class BytesReceiver
public class FooGUI
{
     BytesReceiver _receiver = new BytesReceiver();

     //somewhere, in some function your listener has started
     void Init()
     {
         //we added an event earlier, now attach a handler.
         //a handler is a function, bound to some signature (as defined by the delegate)
         //which will be executed when the event is triggered.

          //so again; we bind a function to the event, which is invoked when the event is 
          //raised.

          //keep in mind: the Invoke take place on the other thread; so the handler
          //runs on that same thread.

          //there is no magical polling taking place: it's just a 
          //function call (from receiver).

         //note: there are various ways to bind a function: I'll use lambda here
         _receiver.MessageProcessed += (sender,e) =>
         {
             //update GUI here. 
         }

         //since there your while loop waits for a long time
         //there must be some non-blocking operation, presumably on another thread.
         _receiver.StartListening();
     }
}

Upvotes: 1

Related Questions