使用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语句,它就可以完美地绘制。

使用await减慢Winforms中的循环

你可以使用你的代码,但是你需要强制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;

如果你直接修改ImageBackgroundImage, InvalidateRefresh就可以了:

((Bitmap)panel1.BackgroundImage).SetPixel(...);
panel1.Invalidate();