我的程序是一个文本游戏,在WindowsForm上模拟带有TextBox的控制台输出。 我试图实现的一个功能是单击一个按钮,它将以一定的速度输出到TextBox,这是通过这种方法实现的:
public static void Write(String text, Color color, TextBox textArea, UI ui)
{
ui.Enabled = false;
foreach (Control b in ui.Controls)//Disable controls
{
if (b.GetType() == typeof(Button))
b.Enabled= false;
}
textArea.ForeColor = color;
foreach (char c in text) //Write the text
{
textArea.AppendText(c.ToString());
if (c == '\n')
{
textArea.AppendText("\n");
Thread.Sleep(100);
}
Thread.Sleep(30);
}
textArea.AppendText("\n");
foreach (Control b in ui.Controls)//Enable controls
{
if (b.GetType() == typeof(Button))
b.Enabled= true;
}
ui.Enabled = true;
}
按钮事件的调用如下:
private void btn1_Click(object sender, EventArgs e)
{
Core.Write("this is button 1", Color.Red, txtDialog, this);
}
private void btn2_Click(object sender, EventArgs e)
{
Core.Write("this is button 2", Color.Green, txtDialog, this);
}
private void btn3_Click(object sender, EventArgs e)
{
Core.Write("this is button 3", Color.Blue, txtDialog,this);
}
private void btn4_Click(object sender, EventArgs e)
{
Core.Write("this is button 4", Color.White, txtDialog,this);
}
我注意到的是,即使按钮和表单在未完成写入逻辑时被禁用,通过在任何给定按钮上单击多次,它将“保存”该单击并在写入后立即输出之前已完成。
我应该怎么做才能消除这个错误?非常感谢
答案 0 :(得分:3)
据我所知,此代码不应该像您预期的那样工作,但它应该像您所描述的那样工作。情况不是错误,这就是Windows的工作原理。 Windows为每个应用程序创建一个消息队列,并将每个事件转发到此应用程序的此消息队列和主线程(WinForms的GUI线程),并按顺序处理消息。
你的代码是做什么的,它会禁用容器(UI?)并隐藏按钮并尝试通过挂起几毫秒来编写一些文本,然后启用所有内容。所有这些都发生在UI线程中,它应该处理消息。
现在,当您点击时,应用会收到点击事件,并运行您的代码,这需要一些时间。当您的代码正在运行时(记住Thread.Sleep
),用户单击您的按钮所在的位置,Windows会将消息发布到您的应用程序队列以便下次处理。何时处理此消息?代码完成后,因为它是处理消息并运行您自己的代码的同一个线程。代码完成后(按钮和UI再次可用),然后处理下一条消息。
尝试使用在另一个主题中运行的BackgroundWorker
。
public static void Write(String text, Color color, TextBox textArea, Form ui)
{
textArea.ForeColor = color;
var handler = new Action<string>(textArea.AppendText);
foreach (char c in text) //Write the text
{
textArea.Invoke(handler, c.ToString());
if (c == '\n')
{
textArea.Invoke(handler, "\n");
Thread.Sleep(100);
}
Thread.Sleep(30);
}
textArea.Invoke(handler, "\n");
}
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Write("this is button 1", Color.Red, textBox1, this);
}