Ace Caserya
Ace Caserya

Reputation: 2935

updating a multi-textbox from a separate thread that runs a looping UDP Listen in C#

I am using C# .net 4.0 VS 2010.

I got a code in a form that basically adds a Task on form load in order to run a UDP Listener (on infinite loop). Whenever the Listener gets something from UDP socket, i add a line and the message to the multiline-textbox (this.textBox4.Text).

However i get an exception saying "Cross-thread operation not valid: "Contol 'textBox4' accessed from a thread other than the thread it was created on."

I didn't want to end the loop just to pass the value. Is there a way to do this? Here are my codes:

    //main form load menu
    private void frm_Menu_Load(object sender, EventArgs e)
    {
        Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
        .... // more code here                      
    }

    //async function
    public Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
    {
        return Task.Factory.StartNew<int>(() =>
        {
            if (WhatToDo == "OpenYourEars")
                goListening();
            ... // more codes here
            return 1;
        });
    }

   //Listening on UDP socket
    public void goListening()
    {
        bool done = false;
        UdpClient listener = new UdpClient(listenPort);
        IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
        string received_data;
        byte[] receive_byte_array;
        try
        {
            while (!done)
            {
                receive_byte_array = listener.Receive(ref groupEP);
                received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);

                // display on TextBox4
                this.textBox4.Text = "a\r\nb";
                this.textBox4.Text = received_data.ToString().Trim();
            }
        }
        catch (Exception e)
        {
            //gives "Contol 'textBox4' accessed from a thread other than 
            //the thread it was created on." when receiving a message.
            MessageBox.Show(e.ToString());
        }
        listener.Close();
    }

Version 2 - After answers by @cremor and @George87

    private void frm_Menu_Load(object sender, EventArgs e)
    {
        MyValue = "Menu,7";
        Task<int> Listening = DoWorkAsync(1, "OpenYourEars");
        .... // more code here                      
    }

    private Task<int> DoWorkAsync(int milliseconds, string WhatToDo)
    {
        return Task.Factory.StartNew<int>(() =>
        {
            if (WhatToDo == "OpenYourEars")
                goListening();
            .... // more codes here
            return 1;
        });
    }

    //Listening
    private void goListening()
    {
        bool done = false;
        UdpClient listener = new UdpClient(listenPort);
        IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
        string received_data;
        byte[] receive_byte_array;
        try
        {
            while (!done)
            {
                receive_byte_array = listener.Receive(ref groupEP);
                received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);

                string aa = received_data.ToString().Trim();
                if ( aa != "")
                {
                    SetText("a\r\nb");
                    SetText(received_data.ToString().Trim());
                    aa = "";
                }
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());               
        }
        listener.Close();
    }

    private delegate void SetTextCallback(string text);
    private void SetText(string text)
    {
        try
        {
              if (this.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.BeginInvoke(d, new object[] { text });
            }
            else
            {
                SetText(text);
            }
            this.textBox4.Text = text;
        }
         catch (Exception e)
         {
             MessageBox.Show(e.ToString());
         }

    }
    ....

Upvotes: 0

Views: 941

Answers (3)

NeddySpaghetti
NeddySpaghetti

Reputation: 13495

You can try changing your async function to use the current syncronisation context

 return Task.Factory.StartNew<int>(() =>
    {
        if (WhatToDo == "OpenYourEars")
            goListening();
        return 1;
    },
    new CancellationToken(),
    TaskCreationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext());

Upvotes: 0

George87
George87

Reputation: 92

Normally when using c# and use multi threading you should use delegates to make things work and not violate Cross-thread politics. In other words you are not allowed to use objects defined in one thread from other threads. To make this happen you should use delegates to force the owning thread to perform the task for the calling thread.

Instead of:

  // display on TextBox4
  this.textBox4.Text = "a\r\nb";

you could use this: define this methods:

delegate void SetTextCallback(string text);
private void SetText(string text)
{
   if (this.InvokeRequired)
   {
      SetTextCallback d = new SetTextCallback(SetText);
      this.Invoke(d, new object[] { text });
   }
   else
   {
      SetText(text);
   }
   this.textBox1.Text = text;     
}

and call them from the tread like this

SetText("a\r\nb");

Upvotes: 0

cremor
cremor

Reputation: 6886

UI controls can only be changed by the thread they were created in. You need to check InvokeRequired (WinForms) or Dispatcher.CheckAccess() (WPF) and then call Invoke/BeginInvoke.

Upvotes: 1

Related Questions