运行 C# 中带时间间隔的 for 循环

Running a for loop with time interval in C#

我正在尝试制作一个简单的 C# 程序来掷两个骰子 100 次。对于每次掷骰,主窗口中的骰子图像和一些标签应该发生变化。为了让每一卷都被看到,我需要某种时间控制。当我的循环运行时(由点击事件触发),window 只是冻结了一会儿,然后显示第 100 卷。所以看起来循环在后台 运行(没有抛出异常),但图像没有在 window 中更新。

使用 Thread.Sleep(500) 是我最接近这项工作的方法。我见过一些计时器解决方案,但是 none 允许 for 循环。每当我尝试使用计时器时,我都会遇到线程问题。

循环:

for (int i = 0; i < 100; i++)
{
    rollNumber++;
    labelRollNumber.Content = rollNumber;

    //creates two random integers to use for Image parameters
    int rdmInt1 = rdm.Next(0, 6);
    int rdmInt2 = rdm.Next(0, 6);

    //Changes dice images
    Image(rdmInt1, rdmInt2);

    Thread.Sleep(500);
}

So it appears that the loop is running in the background

相反,它直接在 UI 线程上运行并阻止您尝试显示图像。让它进入睡眠状态也无济于事,因为那样你的应用程序什么都不做,一切都在睡觉。巧合的是,它在 500 毫秒内起作用 "better"。

这个问题有很多解决方法,我给你看其中的一个。

首先,我会将此逻辑封装到 class,但您不必这样做。

class Dice
{
    // event fired when dice is rolled
    public event EventHandler<DiceEventArgs> OnRolled;

    public void Roll(int xTimes)
    {
        int rollNumber = 0;
        var rnd = new Random();

        for (int i = 0; i < xTimes; i++)
        {
            rollNumber++;
            int rdmInt1 = rnd.Next(0, 6);
            int rdmInt2 = rnd.Next(0, 6);

            if (OnRolled != null)
                OnRolled(null, new DiceEventArgs(rollNumber, rdmInt1, rdmInt2));

            Thread.Sleep(1000);
        }
    }
}

现在那些提供有关当前掷骰子的所有信息的特殊事件参数

class DiceEventArgs : EventArgs
{
    private int rollNumber;
    private int dice1;
    private int dice2;

    public int RollNumber { get { return rollNumber; } }
    public int Dice1 { get { return dice1; } }
    public int Dice2 { get { return dice2; } }

    public DiceEventArgs(int rollNumber, int dice1, int dice2)
    {
        this.rollNumber = rollNumber;
        this.dice1 = dice1;
        this.dice2 = dice2;
    }
}

现在 UI 部分

private async void button1_Click(object sender, EventArgs e)
{
    var dice = new Dice();
    dice.OnRolled += Dice_OnRolled;
    await Task.Run(() => dice.Roll(20));
}

void Dice_OnRolled(object sender, DiceEventArgs args)
{
    this.Invoke(new Action(() => { button1.Text = args.RollNumber.ToString(); }));
}

这样你 GUI 将始终保持响应。我通过替换开始滚动的按钮的 Text 属性 对其进行了测试,但您可以很好地调用图像更改。请注意,您必须使用 Invoke 调用它,因为可以从不同的线程触发事件。