关于asp.net:如何从C#中的后台线程刷新Windows窗体中的标签?

How can I refresh a label in Windows Forms from a background thread in C#?

本问题已经有最佳答案,请猛点这里访问。

我有一个线程与主窗体(UI)并行运行。它所做的(目前)就是每秒钟递增一个计数器。我想在Windows窗体中使用标签显示计数器的值。有可能吗?当我尝试以下代码时,我在showValue方法中得到一个编译错误。我必须声明ShowValue"static",以便从后台线程调用它。但是如果我这样做,我就不能使用"this."来访问showValue表单1中的标签。这是正确的方法吗?任何小费都将不胜感激,谢谢!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    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;
                }
            }
        }
    }

然后在我的Form1课程中,我有:

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

  }

第一个问题是获取表单实例,如果调用表单上没有表单实例,则会导致application.openforms属性如下:

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

第二个问题是,您需要将方法修改为实例方法,并将其从跨线程异常中保存,修改方法如下:

1
2
3
4
5
6
7
8
9
10
11
public void ShowValue(int num)
{
    if (label7.InvokeRequired)
    {
        label7.BeginInvoke((MethodInvoker)delegate { label7.Text = num.ToString(); });
    }
    else
    {
        label7.Text = num.ToString();
    }
}


您不能从不同的线程随机访问GUI元素。对您的问题的简短回答是:使用现有结构。

  • 如果你只是想经常做一些事情,那就用一个计时器。当时间到了,它将通知您的主线程(即"拥有"GUI),您可以在那里更新GUI元素。
  • 如果您真的想创建自己的线程,请使用BackgroundWorker。它将提供线程安全事件,您可以从中更新GUI元素。


在静态方法ShowValue(int num)中不能引用局部变量(this.label7

您的方法应该如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
public void ShowValue(int num)
  {

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

  }

在此代码中,将对窗体的静态调用替换为实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   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;
                }
            }
        }

编辑

您可能希望在方法count_secs()之外除去窗体实例。


不一定要使ShowValue函数保持静态。将其保留为非静态的,并用以下代码替换逻辑行Form1.ShowValue(num2)

1
2
3
4
 if (label1.InvokeRequired)
       label1.BeginInvoke(new Action(() => ShowValue(num2)));
 else
      label1.Invoke(new Action(() => ShowValue(num2)));


两个问题:

  • 不能从静态上下文使用this引用。
  • 无法从后台线程更新用户界面。
  • 解决:

  • 将方法ShowValue标记为实例方法(即去掉static)
  • 使用背景工作者或阅读这个解释得很好的问题