运行 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
调用它,因为可以从不同的线程触发事件。
我正在尝试制作一个简单的 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
调用它,因为可以从不同的线程触发事件。