0

已删除帖子并有代码:

namespace StackOverflowQuestion
{
  class Program
  {
        static void Main (string[] args)
        {
              var cw = new ConsoleWriter ();

              for (int i = 0; i <= 5000000; i++)
              {
                    cw.Write (i);
              }

              Console.ReadKey ();
        }
  }

  class ConsoleWriter
  {
        private Stopwatch sw = new Stopwatch ();

        public ConsoleWriter ()
        {
              sw.Start ();
        }

        public void Write (int pNumber)
        {
              if (sw.ElapsedMilliseconds >= 50)
              {
                    Console.WriteLine (pNumber);
                    sw.Restart ();
              }
        }
  }
}

并输出:
305940 651171
1002965
1358665
1715740
2069602
2419054
2772833
3127880
3485054
3844335
4204016
4557912
4913494

所以一切正常。在此示例中,ConsoleWriter 在控制台上显示数字,但它可以显示在控制表面中。如您所见,即使我调用了 5000000 次 Write 方法,它也只会在至少 50 毫秒后更新 UI。很好,但请注意在许多情况下最后一个值 5000000 不会显示。如何解决?我应该使用每 50 毫秒调用一次事件并检查要写入的值是否更改的类(线程)?

4

2 回答 2

4

你可以使用计时器

Timer _timer;

public void StartTimer()
{
    _timer = new Timer();
    _timer.Interval = 100; // 100 ms = 0.1 s
    _timer.Tick += new EventHandler(timer_Tick);
    _timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
    myControl.Number = i;
}

在控件中应该有类似的东西

private int _number;    
public int Number
{
    get { return _number; }
    set
    {
        if (value != _number) {
            _number = value;
            Invalidate();
        }
    }
}

的调用Invalidate()将触发Paint事件。您的绘画逻辑应该在OnPaint方法中:

protected override void OnPaint(PaintEventArgs e)
{
    ... paint here
}

但原因是 for 循环本身会冻结应用程序。您可以使用第二个计时器以比显示计数器更快的间隔更新计数器。在 UI-tread 上运行的每个代码(如果您愿意,也可以是主线程)将冻结您的 UI,直到它终止。在单独的线程中在后台完成繁重工作的一种简单方法是使用BackgroundWorker. 后台工作人员自动在 UI 线程和工作线程之间切换,并允许您向 UI 线程报告进度。

您也可以手动启动线程以更新计数器。如果这将是唯一更改数字的线程,则不需要同步机制。但永远不要从 UI 线程之外的另一个线程访问 UI(表单或控件)。

这是一个完整的非阻塞解决方案,使用另一个线程进行计数

Timer _timer;
int _counter;
System.Threading.Thread _worker;

public frmTimerCounter()
{
    InitializeComponent();
    _worker = new System.Threading.Thread(() =>
    {
        while (_counter < 10000000) {
            _counter++;
            System.Threading.Thread.Sleep(20);
        }
    });
    _worker.Start();
    StartTimer();
}

public void StartTimer()
{
    _timer = new System.Windows.Forms.Timer();
    _timer.Interval = 100; // 100 ms = 0.1 s
    _timer.Tick += new EventHandler(timer_Tick);
    _timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
    // I used a Label for the test. Replace it by your control.
    label1.Text = _counter.ToString();
}
于 2013-01-08T21:20:58.307 回答
2

你没有发布任何代码,但我可以猜到它的样子。您在属性设置器中做太多工作。这个 for() 循环永远不会超过一毫秒,太短以至于不会注意到 GUI 冻结。

您可以通过遵循控件重绘自身的标准方式来实现这一点。这是懒惰。您可以通过调用 Invalidate() 方法来获得它。像这样:

class MyControl : Control {
    private int number;
    public int Number {
        get { return this.number; }
        set {
            if (value != this.number) this.Invalidate();
            this.number = value;
        }
    }
    protected override void OnPaint(PaintEventArgs e) {
        // TODO: paint number
        //...
        base.OnPaint(e);
    }
}

您现在还会发现其他内容,不再使用该 for() 循环。一开始就没有,人类不可能看到现代处理器增加数字的惊人速度。因此,您现在将其替换为:

myControl.Number = 50000;

如果您实际上是想让人眼看到数字增加,那么您将不得不放慢速度。大约每 50 毫秒不超过一次,大约是变化变成模糊的点。这需要一个定时器。

于 2013-01-08T21:37:50.500 回答