我正在尝试制作一个简单的C#程序,它可以滚动两个骰子100次。对于每个卷筒,骰子图像和一些标签应在MainWindow中更改。为了看到每个卷,我需要某种时间控制。当我的循环运行(由单击事件触发)时,窗口会冻结一段时间,然后显示第100次滚动。所以看起来循环在后台运行(没有抛出异常),但是图像没有在窗口中更新。
使用Thread.Sleep(500)是我最接近这项工作的。我见过一些计时器解决方案,但没有一个允许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);
}
答案 0 :(得分:0)
所以看起来循环在后台运行
相反,它直接在UI线程上运行并阻止您的注意事项显示图像。让它进入睡眠也无济于事,因为那时你的应用程序什么都不做,一切都在睡觉。这只是一个巧合,500毫秒它的工作“更好”。
这个问题有很多解决方案,我会告诉你其中一个。
首先,我将此逻辑封装到一个类中,但您不必这样做。
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);
}
}
}
现在这些特殊事件args提供有关当前骰子滚动的所有信息
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
来调用它,因为可以从不同的线程触发事件。