文字没有更新

时间:2013-09-05 16:33:00

标签: c# multithreading forms

我有两个类; - 一个名为Form1.cs,另一个名为NoteThreader.cs,以下是这两个类的内容:

Form1.cs中:

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;
using System.Threading;

namespace HitMachine
{
 public partial class Form1 : Form
 {
    public Form1()
    {
        InitializeComponent();
    }

    public void updateText(string theText)
    {
        label1.Text = theText;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        XML.LoadMP3.loadFile(XML.XmlParser.ParseDocument("music"));
        Threading.NoteThreader.createTimer();
    }
  }
}

NoteThreader.cs:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;

    namespace HitMachine.Threading
    {
        public class NoteThreader
        {
            public static Thread createTimer()
            {
                Thread t = new Thread(threadDeligation);
                t.Start();

                return t;
            }

            public static int time = 0;

            static void threadDeligation()
            {
                for (; ; )
                {
                    Form1 a = new Form1();
                    a.updateText(time);
                    time++;
                }
            }
        }
    }

代码运行时没有任何异常或错误;但它不会更新文本。我猜这是因为调用方法的线程与更新文本的线程不同?

我试图调用它但未能这样做。我在threadDeligation中运行了一个MessageBox,并且工作正常。

3 个答案:

答案 0 :(得分:2)

threadDeligation方法中,每次执行循环时都会创建Form1的新实例。这不会更改您正在查看的Form1实例中的文字。

您需要将Form1的正确实例传递给createTimer方法(然后将其传递给threadDeligation方法)。

你可以这样做:

private void Form1_Load(object sender, EventArgs e)
{
    XML.LoadMP3.loadFile(XML.XmlParser.ParseDocument("music"));
    Threading.NoteThreader.createTimer(this);
}

然后:

public static Thread createTimer(Form1 theForm)
{
    Thread t = new Thread(() => threadDeligation(theForm));
    t.Start();
   return t;
}

public static int time = 0;

static void threadDeligation(Form1 theForm)
{
    for (; ; )
    {
         theForm.updateText(time);
         time++;
    }
}

这应该为您提供如何在代码中传递对象的一般概念, 但它将 NOT 工作,因为不支持从非ui线程更新UI元素。您需要改为使用BackgroundWorker类。

答案 1 :(得分:1)

for (; ; )
{
    Form1 a = new Form1();
    a.updateText(time);
    time++;
}
  1. 每次迭代都会创建新的Form1对象。
  2. a.Show();在哪里?
  3. 如果要显示计时器的值,则应该继续引用相应的Form1对象。

答案 2 :(得分:1)

所以这里有很多问题。首先是其他答案提到的显而易见的问题。您正在创建Form1的新实例并调用其方法,从而修改其标签。您没有修改向用户显示的表单;它没有变化。

如果您要获取正在使用的表单实例,那么您将遇到从另一个线程修改表单的问题。这是不允许的。在winform编程中,您只能从创建它们的线程中访问Control个对象;否则他们会犯错。

要解决此问题,您可以调用主UI线程来执行更新。但是,您现在处于可以发送命令以更新标签的位置,就像您的计算机可能发送它们一样快。那是......非常,非常非常快。如此之快,以至于屏幕根本无法更新;由于您发送的命令数量庞大,您将开始滞后,甚至冻结。

似乎你想创建某种临时时钟,你可以在X时间间隔内增加一个数字。让我们说一次(但你可以很容易地将它改为10毫秒,或其他)。要在X时间间隔内做一些事情,我们要使用Timer

void Form1_Load(object sender, EventArgs e)
{
    var timer = new System.Windows.Forms.Timer();
    timer.Interval = 1000;
    timer.Tick += timer_Tick;
    timer.Start();
}

private int time = 0;
void timer_Tick(object sender, EventArgs e)
{
    label1.Text = time.ToString();
    time++;
}

如果不是每隔X个时间间隔执行一次你真正想要做的事情,不管你的命名约定,都要做一些富有成效的工作并根据工作进度更新用户界面,那么它就是一个不同的问题。这里的一个好选择是使用Progress类来更新UI以及一些非UI任务的进度。它可能看起来像这样:

void Form1_Load(object sender, EventArgs e)
{
    Progress<string> progress = new Progress<string>(updateText);
    Thread thread = new Thread(() => NoteThreader.DoWork(progress));
    thread.Start();
}

public class NoteThreader
{
    public static void DoWork(IProgress<string> progress)
    {
        for (int i = 0; i < 10; i++)
        {
            Thread.Sleep(1000);//placeholder for real work
            progress.Report(i.ToString());
        }
    }
}