【问题标题】:Textbox delay events文本框延迟事件
【发布时间】:2018-05-13 01:50:54
【问题描述】:

我有一个选项表单,用户必须在其中输入小游戏的参数,从 8 到 32。我的问题是,一旦我开始输入,如果我插入一个低于 8 的数字(我想输入20,例如),只要我输入 2 并将其变为 8,事件就会激活。

private void TXBheight_TextChanged(object sender, EventArgs e)
    {

        if(int.Parse(TXBheight.Text) < 8)
        {
            TXBheight.Text = "8";
        }
        else if (int.Parse(TXBheight.Text) > 32)
        {
            TXBheight.Text = "32";
        }
    }

有什么简单的方法可以延迟,或者等到我完成输入?

对于那些认为这个问题可能重复的人,我看了一下,可能的答案来自 6 年前。在那段时间里,语言和编译器不断发展,所以也许我们都可以学习一些新的东西

【问题讨论】:

  • text_changed 事件在每次文本更改时触发。
  • 是的,这就是为什么我需要延迟,我不能提示一个完整的数字。
  • 为什么不创建一个任务并在每次文本更改时检查它。如果它正在运行,那么如果不在那里做你的事情,则什么也不做
  • 您不能延迟事件,最好添加标签并根据文本框中输入的内容进行更新
  • 可以限制NumericUpDown控件、ComboBox、ListBox等中的输入范围

标签: c# .net winforms


【解决方案1】:

不要使用TextChanged 事件,而是使用TextBox _Validating 事件和_Validated 事件。 _Validating 事件仅在文本框失去焦点时触发,即当用户单击另一个控件时,例如,Button 或另一个 TextBox。发生这种情况时,将触发 _Validating 事件,并测试文本框中的值。如果无效,则取消_Validating 事件。如果它有效,则不要取消_Validating event,因此会触发_Validated 事件。在_Validated event 中,您在输入数据有效时执行您需要执行的操作。当输入数据无效时,使用errorprovider 通知用户。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        errorProvider1.SetError(TXBheight, "");

        //NEW NEW NEW
        buttonCancel.CausesValidation = false;
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        // do what is needed when the button is clicked
    }

    private void TXBheight_Validating(object sender, CancelEventArgs e)
    {

        errorProvider1.SetError(TXBheight, "");

        if (String.IsNullOrEmpty(TXBheight.Text))
        {
            errorProvider1.SetError(TXBheight, "Height is a required field");
            e.Cancel = true;
            return;
        }

        if (int.Parse(TXBheight.Text) < 8)
        {
            errorProvider1.SetError(TXBheight, "Height must be GE 8");
            e.Cancel = true;
            return;
        }

        if (int.Parse(TXBheight.Text) > 32)
        {
            errorProvider1.SetError(TXBheight, "Height must be LE 32");
            e.Cancel = true;
            return;
        }

    }

    private void TXBheight_Validated(object sender, EventArgs e)
    {
        //this event is fired when the data is valid, i.e., 
        // if e.Cancel in the _Validating method is NOT set to cancel

    }

    //NEW NEW NEW
    private void ButtonCancel_Click(object sender, EventArgs e)
    {
        AutoValidate = AutoValidate.Disable;
        Close();
    }

    // NEW #2

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (e.CloseReason == CloseReason.UserClosing)
        {
            DialogResult result = MessageBox.Show("Do you really want to exit?", "Dialog Title", MessageBoxButtons.YesNo);
            if (result == DialogResult.Yes)
            {
                Environment.Exit(0);
            }
            else
            {
                e.Cancel = true;
            }
        }
        else
        {
            e.Cancel = true;
        }
    }
}

【讨论】:

  • 这也是一种可能的解决方案,但我个人不喜欢Validating事件。当您没有正确的值时,您无法离开该字段,甚至无法关闭应用程序。
  • @Julo:您可以使用CancelEventArgs 解决应用程序问题。请参阅Cancel validation event on custom UserControl when form closing 的已接受答案。您可以通过向用户提供Cancel button 来克服现场问题,然后使用disable validation of errorprovider when click cancel button 中的技术(参见两个答案)
  • 我知道,但是当你写一个严格的验证函数时,你不能退出字段(焦点将保持在控件上)。当您按下窗口关闭按钮时,应用程序会尝试离开当前控件,但这会被取消并且焦点会返回到控件,您需要在此处填充正确的值。但是第二个问题也是,你可以在控件中写任何东西(在这种情况下是字符),只有当你试图离开控件时它才会报告错误。因此,我总是使用方法,在更改 (支持不完整值) 和休假时检查值。
  • @Julo:我刚刚修改了我的代码并测试了在检测到无效值(因为文本框失去焦点)并且用户单击取消按钮时关闭应用程序(或表单)的能力.请参阅我上面的代码。除此之外,在用户离开该字段之前,应用程序无法知道用户已完成输入数据
  • 除此之外,应用程序无法知道用户已完成输入数据,直到他们离开该字段,方法是切换到另一个文本框,单击“我完成了,请验证我的所有输入”按钮,或点击“我放弃”按钮
【解决方案2】:

简单的答案是: (C# 7 风格)

public partial class Form1 : Form
{
  public Form1()
  {
    InitializeComponent();
    this.textBox1.TextChanged += TextBox1_TextChanged;
    this.textBox1.Leave += TextBox1_Leave;
  }

  private void TextBox1_TextChanged(object sender, EventArgs e)
  {
    string text = this.textBox1.Text;
    if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out int number))
    {
      this.textBox1.Text = "";
      return;
    }

    if (number > 32)
    {
      this.textBox1.Text = "32";
    }
  }

  private void TextBox1_Leave(object sender, EventArgs e)
  {
    string text = this.textBox1.Text;
    if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.CurrentCulture, out int number))
    {
      this.textBox1.Text = "8";
      return;
    }

    if (number > 32)
    {
      this.textBox1.Text = "32";
    }

    if (number < 8)
    {
      this.textBox1.Text = "8";
    }
  }

我通常通过控制按下的键和文本更改(包括粘贴)来检查窗口的正确内容。不幸的是,我只有 Borland C++ Builder 和 VS6 的代码在工作。重新创建这段代码并不是那么简单(代码太多),因此只有简单的答案。

【讨论】:

    【解决方案3】:

    使用微软的响应式框架,这变得很容易。只需这样做:

    private void Form1_Load(object sender, EventArgs e)
    {
        IObservable<long> query =
            Observable
                .FromEventPattern<EventHandler, EventArgs>(
                    h => TXBheight.TextChanged += h,
                    h => TXBheight.TextChanged -= h)
                .Select(x => Observable.Timer(TimeSpan.FromMilliseconds(250.0)))
                .Switch()
                .ObserveOn(this);
    
        IDisposable subscription = query.Subscribe(ep =>
        {
            if (int.Parse(TXBheight.Text) < 8)
            {
                TXBheight.Text = "8";
            }
            else if (int.Parse(TXBheight.Text) > 32)
            {
                TXBheight.Text = "32";
            }
        });
    }
    

    现在,在您输入最后一个字符后,您的代码运行前会有 250.0 毫秒的延迟。如果在 250.0 毫秒之前输入了一个新字符,那么新的计时器将启动,而旧的计时器不会触发。

    .ObserveOn(this) 代码将计时器编组回 UI 线程。

    只需 NuGet“System.Reactive”和“System.Reactive.Windows.Forms”。还要在班级顶部添加using System.Reactive.Linq;

    【讨论】:

    • 优雅(即使它有点寻找问题的解决方案),但我认为“计时器”方法明显失败的一个例子是邮政编码字段。我输入邮政编码的第一个数字,意识到我不知道邮政编码是什么,打开浏览器查找邮政编码,与此同时,即使我在,应用程序对我“超时”未完成输入数据。这很烦人:-)
    • @NovaSysEng - 谢谢。我同意这可能很烦人,但为什么这更适用于邮政编码?
    • 邮政编码只是一个例子。我只是认为使用“计时器方法”来解决使用TextChanged 事件验证用户输入引起的问题只会导致更多问题。请参阅我对这个问题的回答 - 我认为使用 ValidatingValidated 事件是更好的解决方案。您只需包含允许用户取消操作的代码,即使输入无效。
    • @NovaSysEng - 调用 subscription.Dispose() 将取消代码。 OP确实询问了有关延迟的问题。这就是我的答案所针对的。
    【解决方案4】:

    你可以使用return作为你的断点,当用户点击回车然后你运行你的代码。

    您可以将它与 KeypressEvent 一起使用。

    private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        char ch = e.KeyChar; // Getting the Key that was pressed
    
        if (ch == 13) // Checking if it equal to 13 ASCII code for return 
        {
            if (int.Parse(textBox1.Text) < 8)
            {
                textBox1.Text = ""; // emptying the textbox
                textBox1.AppendText("8"); // using AppendText() to keep the cursor at the end
            }
            else if (int.Parse(textBox1.Text) > 32)
            {
                textBox1.Text = "";
                textBox1.AppendText("32");
            }
            e.Handled = true; // To say that the event was handled.
        }
    }
    

    【讨论】:

    • 不错,工作正常。但是会继续寻找更流畅的方式,而不需要一直按回车。
    • 你想要做的事情是不可能的,你必须使用某种类型的触发器,因为一个数字与两位或三位数字相同,你不知道用户将要插入的内容。
    【解决方案5】:

    为什么不创建任务并在执行前检查它是否完成?

    private Task task; //declare it at the top
    
    private void TXBheight_TextChanged(object sender, EventArgs e)
    {
       if(task?.Status == TaskStatus.Running) return;
    
       task =  Task.Run( () =>
       {
            if(int.Parse(TXBheight.Text) < 8)
            {
                TXBheight.Text = "8";
            }
            else if (int.Parse(TXBheight.Text) > 32)
            {
                TXBheight.Text = "32";
            } 
       });
    }
    

    【讨论】:

      猜你喜欢
      • 2016-04-19
      • 1970-01-01
      • 2012-07-23
      • 1970-01-01
      • 2015-11-21
      • 2011-01-17
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多