使用await减慢Winforms中的循环
本文关键字:循环 Winforms await 减慢 使用 | 更新日期: 2023-09-27 18:18:01
我在Windows窗体应用程序中有一个循环,它以特定的形状绘制像素,但我想让用户看到它被绘制。
正在讨论的循环:
public async void Bar()
{
for (int i = 0; i < 10000; i++)
{
await Task.Delay(100);
Foo();
}
}
Bar()
只是从UI线程的非静态函数中调用。
Foo()
应该在位图上绘制一个像素,并执行(根据调试器),但实际上不绘制任何东西。很不寻常。
如果我删除await语句,它就可以完美地绘制。
你可以使用你的代码,但是你需要强制ui线程更新你修改的位图。为此,将这一行添加到您的Foo
:
panel1.Refresh();
这假设您直接写入所示的位图属性(见下文!)。当然,你需要改变panel1
控制持有Bitmap
;下面是使用控件的外部 Bitmap
变量或'内部' Image
(适用于PictureBox
)或BackgroundImage
(适用于Panel
或许多其他)属性的两种基本情况!
如果你想在位图变量上工作,你的Foo
可能看起来像这样:
public void Foo()
{
Bitmap bmp = (Bitmap) panel1.BackgroundImage;
// now do your drawing stuff
bmp.SetPixel(...);
panel1.BackgroundImage = bmp;
panel1.Refresh();
}
虽然这将工作,通常的方式是通过使用Timer
动画。
使用Timer
的关键是打破循环:
将设置向上移动到,可能是Button.Click
,并将其余的移动到Timer.Tick
,可能像这样:
计数器现在在类级别:
int counter = 0;
Tick
保存计数、校验和绘图主体:
private void timer1_Tick(object sender, EventArgs e)
{
if (counter > 1000)
{
timer1.Stop();
}
else
{
angle1 += angle1Modifier;
angle2 += angle2Modifier;
angle3 += angle3Modifier;
DrawPixel(getPoint(angle1, angle2, angle3));
yourCanvasControl.Refresh();
}
}
Click
保持设置并启动循环/定时器:
private void buttonStart_Click(object sender, EventArgs e)
{
counter = 0;
timer1.Interval = 50;
timer1.Start();
}
这可以工作,因为Timer
与' ui线程'相关联,这是一个执行所有显示工作以及接受所有用户输入的线程。
显然,你不应该阻止它,否则你的程序变得迟缓或似乎冻结,但Timer
,而不是Thread.Sleep
将不会阻止ui线程,所以它是一个很好的选择动画(到目前为止,他们在Winforms)。
注意:由于您使用的是SetPixel
,您正在修改Bitmap
,您需要在控件上添加Invalidate()
或Refresh()
,将Bitmap
保留到Tick
以使其显示;让我们看看如何做到这一点:
如果您正在修改外部Bitmap bmp
,您需要重新分配它:
bmp.SetPixel(...);
panel1.BackgroundImage = bmp;
如果你直接修改Image
或BackgroundImage
, Invalidate
的Refresh
就可以了:
((Bitmap)panel1.BackgroundImage).SetPixel(...);
panel1.Invalidate();