【问题标题】:How to update button text after event如何在事件发生后更新按钮文本
【发布时间】:2012-09-26 21:53:14
【问题描述】:

我试图让一个程序发布一堆文本。用户输入文本、消息数量以及必须以多快的速度传递这些消息。当程序很忙时,按钮文本需要是“停止”而不是“开始”。当您在最初启动它后按下按钮强制它停止时,文本会变回“开始”,但是当程序在传递给定数量的消息后停止时不会发生这种情况,即使代码已就位且不会产生错误。

我有一种感觉,这是因为文本由于某种原因没有更新。我试图用 Invalidate() 和 Update() 刷新它,但这不起作用。如何解决这个问题?

代码如下:

private void button1_Click(object sender, EventArgs e)
    {
        if (button1.Text == "Start")
        {
            isEvil = true;
            button1.Text = "Stop";

            Thread t = new Thread(StartTyping);
            t.Start(textBox1.Text);
        }
        else
        {
            isEvil = false;
            button1.Text = "Start";
        }
    }

    private void StartTyping(object obj)
    {
        string message = obj.ToString();
        int amount = (int)numericUpDown2.Value;
        Thread.Sleep(3000);


        for (int i = 0; i < amount; i++)
        {
            if (isEvil == false)
            {
                //////This does NOT work
                //button1.Text = "Start";
                //button1.Invalidate();
                //button1.Update();
                //button1.Refresh();
                //Application.DoEvents();
                break;
            }

            SendKeys.SendWait(message + "{ENTER}");
            int j = (int)numericUpDown1.Value * 10;
            Thread.Sleep(j);
        }
    }

【问题讨论】:

  • 您不能在与 UI 线程不同的线程上更新 UI。这是使用 WPF 还是 Winforms?还有什么版本的.NET?
  • 我不确定我是否喜欢你的意图... isEvil??我猜这是一个恶意程序...
  • @surcapsalot 不,我只是发现了一个有趣且合适的名字。该程序只是用来玩和学习。 KDiTraglia Windows 窗体。 4.0 如果我是正确的。
  • 哈哈,我曾经收到一封带有链接的电子邮件 onclick="RunMaliciousCode();" - 听起来有点像

标签: c# refresh


【解决方案1】:

您有四个答案告诉您从 UI 线程更新 UI 内容,但它们都没有解决您的代码的逻辑流问题。

它没有发生的原因是因为它只在isEvilfalse 时发生for 循环isEvil 何时设置为 false?仅当您单击“停止”时,其他任何地方都没有。

如果您希望按钮在线程完成后返回“开始”,而不单击“停止”,那么您需要在循环之后添加代码来做到这一点,与值无关isEvil 的:(借鉴 VoidMain 的回答)

private void StartTyping(object obj)
{
    string message = obj.ToString();
    int amount = (int)numericUpDown2.Value;
    Thread.Sleep(3000);

    for (int i = 0; i < amount; i++)
    {
        if (isEvil == false)
        {
            if (button1.InvokeRequired)
            {
                button1.BeginInvoke( new Action(() => { button1.Text = "Start"; }) );
            }
            else
            {
                button1.Text = "Start";
            }
            break;
        }

        SendKeys.SendWait(message + "{ENTER}");
        int j = (int)numericUpDown1.Value * 10;
        Thread.Sleep(j);
    }

    if (button1.InvokeRequired)
    {
        button1.BeginInvoke( new Action(() => { button1.Text = "Start"; }) );
    }
    else
    {
        button1.Text = "Start";
    }
}

现在你有重复的代码,所以你可能想把它分成一个单独的方法。

【讨论】:

    【解决方案2】:

    您需要在 UI 线程上才能更新 UI。

    尝试一种称为 SynchronizationContext 的方法。当你用谷歌搜索时,有很多例子。

    如果您在 WPFSilverlight 中,则可以使用 Dispatcher。同样,如果您在 google 或 StackOverflow 中搜索这些关键字,就会有很多示例。

    【讨论】:

      【解决方案3】:

      您必须从 UI 线程更新您的控件。这就是你对 winform 的处理方式。

          for (int i = 0; i < amount; i++)
          {
              if (isEvil == false)
              {
                  button1.Invoke(new Action(() => button1.Text = "Start"));
                  break;
              }
      
              SendKeys.SendWait(message + "{ENTER}");
              int j = (int)numericUpDown1.Value * 10;
              Thread.Sleep(j);
          }
      

      这将一直阻塞,直到 button1 的文本得到更新。如果您不想阻止它,请将Invoke 替换为BeginInvoke

      【讨论】:

      • 您是在后台工作人员还是在 UI 线程中运行此检查?
      • 我想在 UI 线程中,虽然我不确定你的意思。我还是 C# 的初学者。
      【解决方案4】:

      最好的办法是使用BackgroundWorker。在这里添加一个简洁的例子有点太笨拙了,但是有一个decent tutorial from O'Reilly

      【讨论】:

        【解决方案5】:

        类似的东西(未经测试)应该工作:

        private void StartTyping(object obj)
        {
            string message = obj.ToString();
            int amount = (int)numericUpDown2.Value;
            Thread.Sleep(3000);
        
        
            for (int i = 0; i < amount; i++)
            {
                if (isEvil == false)
                {
                    if(button1.InvokeRequired)
                    {
                        button1.BeginInvoke( new Action(() => { button1.Text = "Start"; }) );
                    }
                    else
                    {
                        button1.Text = "Start";
                    }
                    break;
                }
        
                SendKeys.SendWait(message + "{ENTER}");
                int j = (int)numericUpDown1.Value * 10;
                Thread.Sleep(j);
            }
        }
        

        【讨论】:

        • InvokeRequired 不是必需的,我们已经知道我们在不同的线程上,因此无需检查,对于单行 Lambada,您也不需要包含 { }
        • 谢谢,斯科特,我都知道,我把测试放在InvokeRequired 因为,从他发布的代码中我不完全确定它会被调用每个 时间来自同一个地方,至于大括号,为了清楚起见,我还是倾向于把它放在...
        • 它会生成错误:“无法将 lambda 表达式转换为类型 'System.Delegate',因为它不是委托类型”。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-03
        • 1970-01-01
        • 1970-01-01
        • 2021-01-16
        相关资源
        最近更新 更多