0

I have a thread that runs in parallel with the Main Form (UI). All it does (for now) is increment a counter every second. I want to display the value of the counter using a label in Windows Forms. Is that possible? When I try the following code, I get a compile error in the ShowValue method. I have to declare ShowValue "static" so that I can call it from the background thread. But if I do that, I cannot use the "this." to access the label in ShowValue Form1. Is this the correct way to do this? Any tip would be appreciated, thanks!

    private void count_secs()
    {
        while (!stopThread)
        {
            if (stopThread)
            {
                break;
            }
            num2++;                      // increment counter
            Form1.ShowValue(num2);       // display the counter value in the main Form
            try
            {
                Thread.Sleep(1000);      // wait 1 sec.
            }
            catch (ThreadInterruptedException)
            {
                if (stopThread)
                {
                    break;
                }
            }
        }
    }

Then in my Form1 class, I have:

  public static void ShowValue(int num)
  {
        this.label7.Text = num.ToString();    
        // compiler error here: "Keyword 'this' is not valid in a static method.

  }
Ghost Answer
  • 1,458
  • 1
  • 17
  • 41

5 Answers5

1

You can't refer a local variable (this.label7) in the static method ShowValue(int num)

your method should look like this:

public void ShowValue(int num)
  {

       if(label7.InvokeREquired)
       {
           Action a = () => ShowValue(num);
           label7.Invoke(a);
       }
       else
        this.label7.Text = num.ToString();    

  }

in this code, replace the static call to your form with an instance:

   private void count_secs()
    {
         var frm = new Form1(); //create instance
         frm.Show(); // show form

        while (!stopThread)
        {
            if (stopThread)
            {
                break;
            }
            num2++;                      // increment counter

            //use form instance
            frm.ShowValue(num2);       // display the counter value in the main Form
            try
            {
                Thread.Sleep(1000);      // wait 1 sec.
            }
            catch (ThreadInterruptedException)
            {
                if (stopThread)
                {
                    break;
                }
            }
        }

EDIT

You might want to decalre the form instance outside method count_secs()

Jens Kloster
  • 11,099
  • 5
  • 40
  • 54
1

You cannot randomly access GUI elements from different threads. The short answer to your problem is: Use existing structures.

  • If you just want to do things frequently, use a Timer. It will notify your main thread (that "owns" the GUI) when the time is up and you can update the GUI element there.
  • If you really want to create your own thread, use a Backgroundworker. It will offer thread-safe events from which you can update your GUI elements.
Jan
  • 2,168
  • 2
  • 19
  • 28
1

Your first issue is to get the Form Instance, if you don't have the Form instance on your calling form then you cause Application.OpenForms property like:

Form1 frm = Application.OpenForms["Form1"] as Form1;
if(frm != null)
    frm.ShowValue(num2);

Your second issue is that you need to modify your method as instance method and to save it from Cross threaded exception modify it like:

public void ShowValue(int num)
{
    if (label7.InvokeRequired)
    {
        label7.BeginInvoke((MethodInvoker)delegate { label7.Text = num.ToString(); });
    }
    else
    {
        label7.Text = num.ToString();
    }
} 
Habib
  • 219,104
  • 29
  • 407
  • 436
0

Two issues:

  1. You cannot use the this reference from a static context.
  2. You cannot update your UI from a background thread.

Solutions:

  1. Mark the method ShowValue as an instance method (ie. get rid of the static)
  2. Use a background worker or read this question which explains it very well
Community
  • 1
  • 1
ose
  • 4,065
  • 2
  • 24
  • 40
0

It's not mandatory to make the ShowValue function static. Leave it as non static and replace your line of logic Form1.ShowValue(num2) with following code.

 if (label1.InvokeRequired)
       label1.BeginInvoke(new Action(() => ShowValue(num2)));
 else
      label1.Invoke(new Action(() => ShowValue(num2)));
S.N
  • 4,910
  • 5
  • 31
  • 51
  • why would you invoke the label if it is not required? – Jens Kloster May 08 '13 at 08:03
  • Not required. But at least the folk will be able to start looking into a direction of why I use BeginInvoke or Invoke or InvokeRequired, which eventually leads him more reading. – S.N May 08 '13 at 08:08
  • I understand. but the [difference](http://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke) between the two is sync or async invoke. But non of them are relevant if invoke is not required. – Jens Kloster May 08 '13 at 08:11
  • From the initial question, it was evident that the control was created from the UI thread and accessed on the background thread hence InvokeRequired to check thread affinity. And having invoke required will give the freedom of fall back if he decide later to create his label control in other thread instead of UI. Make sense ? – S.N May 08 '13 at 08:25
  • no. sry. I still don't understand why you would invoke `label1` if `label1.InvokeRequired == false`. But it is ok :) you have *a* reason. – Jens Kloster May 08 '13 at 08:31