已删除帖子并拥有代码:
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 ms后更新UI。太棒了,但是在很多情况下请注意不会显示最后一个值5000000。怎么解决?我应该使用一个类(线程),它将每50毫秒调用一次事件,它会检查要写入的值是否已更改?
答案 0 :(得分: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线程报告progess。
您也可以手动启动线程以更新计数器。如果这是唯一更改数字的线程,则不需要同步机制。但是永远不要从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();
}
答案 1 :(得分:2)
您没有发布任何代码,但我可以猜测它的样子。您在属性设置器中执行了 way 过多的工作。这个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毫秒不超过一次,大约是变化变成模糊的点。这需要一个Timer。