鼠标“缓冲”单击禁用按钮

问题描述 投票:2回答:1

我的程序是一个文本游戏,模拟WindowsForm上带有TextBox的控制台输出。我试图实现的一个功能是单击一个按钮,它将以一定的速度输出到TextBox,这是通过此方法atm实现的:

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);
    }

我注意到的是,即使按钮和表单在没有完成写入逻辑时被禁用,通过在任何给定按钮上单击多次,它将“保存”该单击并在写入之后立即输出已经完成了。

我该怎么做才能消除这个错误?非常感谢

c# winforms
1个回答
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);
}
© www.soinside.com 2019 - 2024. All rights reserved.