Hi everyone,

I am facing problems in updating controls using another thread created by another class (not Form1).

I am able to update controls of main form by another thread if that thread was created by Form1 or main form, but when it is created by other class, i am not able to figure out how to solve this problem


I have picturebox in my maiin form. I am keeping one thread for listening TCP connections. Once a tcp connection is established, i m creating object of another class (client.cs) and using this class, I am creating another thread for this newly connected client.

Client will give information to my server and using this information, I have to draw one figure in main form for corresponding client.

I tried creating new instance of form1 but I realised I was wrong so now I am stuck.


Can anyone please tell me what should i do?

Recommended Answers

All 5 Replies

You need to call Invoke on the control. Use this method to set the picture from another thread:

private void SetPicture(Image img)
    {
      if (pictureBox1.InvokeRequired)
      {
        pictureBox1.Invoke(new MethodInvoker(
        delegate()
        {
          pictureBox1.Image = img;
        }));
      }
      else
      {
        pictureBox1.Image = img;
      }
    }
commented: concise and correct, as always. +2

Thanks sknake for reply

Actually my thread is created by other class than form1 so while calling a delegate defined in Form1 from different class, i dont have any reference for Form1.

I am able to load picturebox if thread is created by Form1.


You need to call Invoke on the control. Use this method to set the picture from another thread:

private void SetPicture(Image img)
    {
      if (pictureBox1.InvokeRequired)
      {
        pictureBox1.Invoke(new MethodInvoker(
        delegate()
        {
          pictureBox1.Image = img;
        }));
      }
      else
      {
        pictureBox1.Image = img;
      }
    }

Sure you can.

Here is my Form1:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace daniweb.threadset
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    internal void SetPicture(Image img)
    {
      ThreadStuff.WriteThreadOutputInfo("SetPicture()");
      if (pictureBox1.InvokeRequired)
      {
        pictureBox1.Invoke(new MethodInvoker(
        delegate()
        {
          SetPicture(img);
        }));
      }
      else
      {
        pictureBox1.Image = img;
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      ThreadStuff.WriteThreadOutputInfo("button1_Click()");
      ThreadStuff.SetImageInAnotherThread();
    }
  }
}

Here is another class with no reference to Form1 doing the work:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Drawing;
using System.Windows;
using System.Windows.Forms;

namespace daniweb.threadset
{
  public static class ThreadStuff
  {
    /// <summary>
    /// This creates another thread with no reference to the main form
    /// </summary>
    public static void SetImageInAnotherThread()
    {
      //SetImageInAnotherThread() is called from the main thread and starts another thread
      WriteThreadOutputInfo("SetImageInAnotherThread()");
      new Func<Bitmap>(CreatePicture).BeginInvoke(
        new AsyncCallback(CreatePictureCallback),
        null);
    }

    /// <summary>
    /// Creates the image to set on the main form
    /// </summary>
    /// <returns></returns>
    private static Bitmap CreatePicture()
    {
      //This method is ran a new thread, not the UI thread:
      WriteThreadOutputInfo("CreatePicture()");
      Bitmap result = new Bitmap(50, 50);
      for (int x = 0; x < result.Height; ++x)
        for (int y = 0; y < result.Width; ++y)
          result.SetPixel(x, y, Color.White);
      for (int x = 0; x < result.Height; ++x)
        result.SetPixel(x, x, Color.Red);
      return result;
    }

    private static void CreatePictureCallback(IAsyncResult ar)
    {
      WriteThreadOutputInfo("CreatePictureCallback()");
      AsyncResult result = (AsyncResult)ar;
      Func<Bitmap> del = (Func<Bitmap>)result.AsyncDelegate;
      try
      {
        Bitmap pic = del.EndInvoke(ar);
        if (pic != null)
        {
          Form1 frm1 = (Form1)FindOpenForm(typeof(Form1));
          if (frm1 != null)
          {
            frm1.SetPicture(pic);
          }
        }
      }
      catch { }
    }

    private static Form FindOpenForm(Type typ)
    {
      for (int i1 = 0; i1 < Application.OpenForms.Count; i1++)
      {
        if (!Application.OpenForms[i1].IsDisposed && (Application.OpenForms[i1].GetType() == typ))
        {
          return Application.OpenForms[i1];
        }
      }
      return null;
    }

    public static void WriteThreadOutputInfo(string Method)
    {
      Console.WriteLine(Method + " running on thread: " + Thread.CurrentThread.ManagedThreadId.ToString());
    }

  }
}

Here is the output from the runtime thread output:

button1_Click() running on thread: 10
SetImageInAnotherThread() running on thread: 10
CreatePicture() running on thread: 7
CreatePictureCallback() running on thread: 7
SetPicture() running on thread: 7
SetPicture() running on thread: 10

Thanks Sknake,

using the code u provided, I could change controls on main form using threads created by other class.

The main and important code was the way you created form1 object.
I had no idea about system.remoting namespace.

Is this called .net remoting?


Btw I didnt understand your code completely.

Can you please explain me what these lines are doing?
1) AsyncResult result = (AsyncResult)asyn;
2) Func<Bitmap> del = (Func<Bitmap>)result.AsyncDelegate;
3) new Func<Bitmap>(createpicture).BeginInvoke(new AsyncCallback(createpicturecallback), null);

I think you are doing a asynchronous operation for invoking, right?

In my program, I am doing invoke (not begininvoke). Will that make any difference?

I wrote few client applications to connect with this server and checked threads created for these clients. They were different. So it was just the way I wanted.
I just added ur form1 object creation method and used form1 to call delegates in main form and it worked.

earlier i used to do Form1 form1 = new Form1(); which would generate different object of Form1. ur method was the key.

Thanks again.
In case, I face any problems, i will let u know.

Sure you can.

Here is my Form1:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace daniweb.threadset
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    internal void SetPicture(Image img)
    {
      ThreadStuff.WriteThreadOutputInfo("SetPicture()");
      if (pictureBox1.InvokeRequired)
      {
        pictureBox1.Invoke(new MethodInvoker(
        delegate()
        {
          SetPicture(img);
        }));
      }
      else
      {
        pictureBox1.Image = img;
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      ThreadStuff.WriteThreadOutputInfo("button1_Click()");
      ThreadStuff.SetImageInAnotherThread();
    }
  }
}

Here is another class with no reference to Form1 doing the work:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Messaging;
using System.Drawing;
using System.Windows;
using System.Windows.Forms;

namespace daniweb.threadset
{
  public static class ThreadStuff
  {
    /// <summary>
    /// This creates another thread with no reference to the main form
    /// </summary>
    public static void SetImageInAnotherThread()
    {
      //SetImageInAnotherThread() is called from the main thread and starts another thread
      WriteThreadOutputInfo("SetImageInAnotherThread()");
      new Func<Bitmap>(CreatePicture).BeginInvoke(
        new AsyncCallback(CreatePictureCallback),
        null);
    }

    /// <summary>
    /// Creates the image to set on the main form
    /// </summary>
    /// <returns></returns>
    private static Bitmap CreatePicture()
    {
      //This method is ran a new thread, not the UI thread:
      WriteThreadOutputInfo("CreatePicture()");
      Bitmap result = new Bitmap(50, 50);
      for (int x = 0; x < result.Height; ++x)
        for (int y = 0; y < result.Width; ++y)
          result.SetPixel(x, y, Color.White);
      for (int x = 0; x < result.Height; ++x)
        result.SetPixel(x, x, Color.Red);
      return result;
    }

    private static void CreatePictureCallback(IAsyncResult ar)
    {
      WriteThreadOutputInfo("CreatePictureCallback()");
      AsyncResult result = (AsyncResult)ar;
      Func<Bitmap> del = (Func<Bitmap>)result.AsyncDelegate;
      try
      {
        Bitmap pic = del.EndInvoke(ar);
        if (pic != null)
        {
          Form1 frm1 = (Form1)FindOpenForm(typeof(Form1));
          if (frm1 != null)
          {
            frm1.SetPicture(pic);
          }
        }
      }
      catch { }
    }

    private static Form FindOpenForm(Type typ)
    {
      for (int i1 = 0; i1 < Application.OpenForms.Count; i1++)
      {
        if (!Application.OpenForms[i1].IsDisposed && (Application.OpenForms[i1].GetType() == typ))
        {
          return Application.OpenForms[i1];
        }
      }
      return null;
    }

    public static void WriteThreadOutputInfo(string Method)
    {
      Console.WriteLine(Method + " running on thread: " + Thread.CurrentThread.ManagedThreadId.ToString());
    }

  }
}

Here is the output from the runtime thread output:

button1_Click() running on thread: 10
SetImageInAnotherThread() running on thread: 10
CreatePicture() running on thread: 7
CreatePictureCallback() running on thread: 7
SetPicture() running on thread: 7
SetPicture() running on thread: 10

>>Is this called .net remoting?
No, not really. It is using a concept of remoting but it is not remoting to another application. "Remoting" tends to be calling native code outside of the current application domain (whether that be in another process on another server, or same process different app domain). If you want a detailed explanation i'll give it a shot but its not what you think, I promise :P

>>1) AsyncResult result = (AsyncResult)asyn;
This relates to your question above. I am just casting the IAsyncResult to AsycResulting because I need to call .EndInvoke() to get the return method of the value.

>>2) Func<Bitmap> del = (Func<Bitmap>)result.AsyncDelegate;
Func<TResult> creates a delegate signature with a Bitmap return type. Basically that is equivelant to public delegate void SomeMethod(); . I just find it easier to use Func<> and Action<> than declaring a delegate and remembering the name.

>>3) new Func<Bitmap>(createpicture).BeginInvoke(new AsyncCallback(createpicturecallback), null);
This is just calling a delegate asynchronously. I have added a functionally identical method below to show you:

/// <summary>
    /// This creates another thread with no reference to the main form
    /// </summary>
    public static void SetImageInAnotherThread()
    {
      //SetImageInAnotherThread() is called from the main thread and starts another thread
      WriteThreadOutputInfo("SetImageInAnotherThread()");
      new Func<Bitmap>(CreatePicture).BeginInvoke(
        new AsyncCallback(CreatePictureCallback),
        null);
    }

    public delegate Bitmap CreatePictureDelegate();
    public static void SetImageInAnotherThread2()
    {
      CreatePictureDelegate del = new CreatePictureDelegate(CreatePicture);
      del.BeginInvoke(new AsyncCallback(CreatePictureCallback), null);
    }

They both do the exact same thing.

>>In my program, I am doing invoke (not begininvoke). Will that make any difference?
.BeginInvoke() causes the code to be ran on another thread. I used this to get Bitmap-generating code on to another thread, that way I could show you how to call .Invoke() on your main form to get the code to run on your UI thread.

From your case, as I understand it, you receive a bitmap on the non-UI thread. If this is the case you can ignore all the thread stuff I do and just concern yourself with the pictureBox1.InvokeRequired and pictureBox1.Invoke() lines on the main form. I split out the other class just to get the code to run on another thread.

>>earlier i used to do Form1 form1 = new Form1(); which would generate different object of Form1. ur method was the key
Yes scanning Application.OpenForms can be a wonderful thing ;)

Please mark this thread as solved if you have found an answer to your question and good luck!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.