c#等待用户在文本框中完成输入

本文关键字:输入 文本 等待 用户 | 更新日期: 2023-09-27 18:13:38

在c#中是否有一种方法可以等到用户在文本框中输入完后再输入他们键入的值而不按回车键?

稍微修改了一下这个问题:

好的,我有一个简单的计算器,它乘以2。

下面是我想让它做的:用户在文本框中输入一个值,比如1000,它会自动显示2000。

是这样发生的:一旦用户输入1,它乘以2,输出2。

c#等待用户在文本框中完成输入

我现在将"finished typing"定义为"用户已经输入了一些东西,但在一段时间后没有输入任何东西"。有了这个定义,我写了一个小类,从TextBox派生扩展它的DelayedTextChanged事件。我不能保证它是完整和无bug的,但它满足了一个小烟雾测试。请随意更改和/或使用它。我叫它MyTextBox,因为我现在想不出一个更好的名字。您可以使用DelayedTextChangedTimeout属性更改等待超时时间。默认为10000ms(= 10秒)。

public class MyTextBox : TextBox
{
    private Timer m_delayedTextChangedTimer;
    public event EventHandler DelayedTextChanged;
    public MyTextBox() : base() 
    {
        this.DelayedTextChangedTimeout = 10 * 1000; // 10 seconds
    }
    protected override void Dispose(bool disposing)
    {
        if (m_delayedTextChangedTimer != null)
        {
            m_delayedTextChangedTimer.Stop();
            if (disposing)
                m_delayedTextChangedTimer.Dispose();
        }
        base.Dispose(disposing);            
    }
    public int DelayedTextChangedTimeout { get; set; }
    protected virtual void OnDelayedTextChanged(EventArgs e)
    {
        if (this.DelayedTextChanged != null)
            this.DelayedTextChanged(this, e);
    }
    protected override void OnTextChanged(EventArgs e)
    {
        this.InitializeDelayedTextChangedEvent();
        base.OnTextChanged(e);            
    }                
    private void InitializeDelayedTextChangedEvent()
    {
        if (m_delayedTextChangedTimer != null)
            m_delayedTextChangedTimer.Stop();
        if (m_delayedTextChangedTimer == null || m_delayedTextChangedTimer.Interval != this.DelayedTextChangedTimeout)
        {                
            m_delayedTextChangedTimer = new Timer();
            m_delayedTextChangedTimer.Tick += new EventHandler(HandleDelayedTextChangedTimerTick);
            m_delayedTextChangedTimer.Interval = this.DelayedTextChangedTimeout;
        }
        m_delayedTextChangedTimer.Start();
    }
    private void HandleDelayedTextChangedTimerTick(object sender, EventArgs e)
    {
        Timer timer = sender as Timer;
        timer.Stop();
        this.OnDelayedTextChanged(EventArgs.Empty);
    }
}

另一个简单的解决方案是在表单中添加一个计时器,将Interval属性设置为250,然后使用计时器的tick事件,如下所示:

private void timer1_Tick(object sender, EventArgs e)
{
    timer1.Stop();
    Calculate(); // method to calculate value
}
private void txtNumber_TextChanged(object sender, EventArgs e)
{
    timer1.Stop();
    timer1.Start();
}

如果你使用的是WPF和。net 4.5或更高版本,那么在控件的绑定部分有一个名为"Delay"的新属性。它定义了一个时间范围,在该时间范围之后,源将被更新。

<TextBox Text="{Binding Name, Delay=500}" />

这意味着源只在500毫秒后更新。据我所知,它在文本框结束后输入更新。顺便说一句。这个属性在其他场景中也很有用,例如:列表框等。

我也面临同样的挑战,下面是我的简单方法。

public partial class Form2 : Form
    {
        static int VALIDATION_DELAY = 1500;
        System.Threading.Timer timer = null;
        public Form2()
        {
            InitializeComponent();
        }
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            TextBox origin = sender as TextBox;
            if (!origin.ContainsFocus)
                return;
            DisposeTimer();
            timer = new System.Threading.Timer(TimerElapsed, null, VALIDATION_DELAY, VALIDATION_DELAY);
        }
        private void TimerElapsed(Object obj)
        {
            CheckSyntaxAndReport();
            DisposeTimer();            
        }
        private void DisposeTimer()
        {
            if (timer != null)
            {
                timer.Dispose();
                timer = null;
            }
        }
        private void CheckSyntaxAndReport()
        {            
            this.Invoke(new Action(() => 
            {
                string s = textBox1.Text.ToUpper(); //Do everything on the UI thread itself
                label1.Text = s; 
            }
                ));            
        }
    }

你可以处理文本框的LostFocus事件,该事件将在每次用户完成输入并从文本框导航时触发。以下是关于LostFocus的文档:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.lostfocus.aspx

然而,我不确定你到底想在这里做什么,因为问题不是很清楚"finish"是什么意思。

在UWP中,我通过创建静态lastTimeOfTyping并检查"TextChanged"事件发生的时间来进行延迟检查。当新的"TextChanged"时间匹配时,它将等待静态lastTimeOfTyping匹配,然后执行所需的函数。

    private const int millisecondsToWait = 500;
    private static DateTime s_lastTimeOfTyping;
    private void SearchField_OnTextChanged(object sender, TextChangedEventArgs e)
    {
        var latestTimeOfTyping = DateTime.Now;
        var text = ((TextBox)sender).Text;
        Task.Run(()=>DelayedCheck(latestTimeOfTyping, text));
        s_lastTimeOfTyping = latestTimeOfTyping;
    }
    private async Task DelayedCheck(DateTime latestTimeOfTyping, string text)
    {
        await Task.Delay(millisecondsToWait);
        if (latestTimeOfTyping.Equals(s_lastTimeOfTyping))
        {
            // Execute your function here after last text change
            // Will need to bring back to the UI if doing UI changes
        }
    }

作为异步扩展方法。改编自Grecon14的回答

注意:这没有考虑光标位置的变化,所以如果用户使用方向键移动,但实际上没有改变文本,它将返回true。问题是"打完字"。我不确定移动光标是否构成真正的打字,也许?作为一个用户,我希望它包含这个活动。不幸的是,它需要比下面更复杂的接口功能。如果你需要的话,可以参考SurfingSanta的答案,它有一个按键订阅。

public static class UIExtensionMethods
{
    public static async Task<bool> GetIdle(this TextBox txb)
    {
        string txt = txb.Text;
        await Task.Delay(500);
        return txt == txb.Text;
    }
}

用法:

if (await myTextBox.GetIdle()){
    // typing has stopped, do stuff
}

我不知道onChange()是否只存在于旧版本的c#中,但我找不到它!

当用户按下回车键或从文本框中跳出选项卡时,下面的工作用于检测,但仅在更改一些文本之后:

    //--- this block deals with user editing the textBoxInputFile --- //
    private Boolean textChanged = false;
    private void textBoxInputFile_TextChanged(object sender, EventArgs e) {
        textChanged = true;
    }
    private void textBoxInputFile_Leave(object sender, EventArgs e) {
        if (textChanged) {
            fileNameChanged();
        }
        textChanged = false;
    }
    private void textBoxInputFile_KeyDown(object sender, KeyEventArgs e) {
        if (textChanged & e.KeyCode == Keys.Enter) {
            fileNameChanged();
        }
        textChanged = false;
    }
    //--- end block  --- //

您可以使用onChange()事件文本框。如果在文本框中更改了文本,则检查输入的值是否为数字,并根据另一个值计算总价值。

您希望对所讨论的文本框使用Leave或LostFocus事件的句柄。我假设你正在使用WinForm,即使你没有在你的问题中说明。

如果基于tab或return之类的击键触发事件怎么办?

我的一个同事提出了一个使用Rx和事件节流的解决方案:

var FindDelay = 500;//milliseconds
//textBox is your text box element
Observable.FromEventPattern<EventArgs>(textBox, "TextChanged")
    .Select(ea => ((TextBox) ea.Sender).Text)
    .DistinctUntilChanged()
    .Throttle(TimeSpan.FromMilliseconds(FindDelay))
    .Subscribe(text => { 
        //your handler here 
    });

理想情况下,像esskar的继承解决方案是可行的,但它不适合设计师,所以为了获得重用,我选择了一个helper样式的side-class:

using System;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Threading.Timer;
    internal class DelayedText : IDisposable
    {
        private readonly EventHandler _onTextChangedDelayed;
        private readonly TextBox _textBox;
        private readonly int _period;
        private Timer _timer;
        public DelayedText(TextBox textBox, EventHandler onTextChangedDelayed, int period = 250)
        {
            _textBox = textBox;
            _onTextChangedDelayed = onTextChangedDelayed;
            _textBox.TextChanged += TextBoxOnTextChanged;
            _period = period;
        }
        public void Dispose()
        {
            _timer?.Dispose();
            _timer = null;
        }
        private void TextBoxOnTextChanged(object sender, EventArgs e)
        {
            Dispose();
            _timer = new Timer(TimerElapsed, null, _period, Timeout.Infinite);
        }
        private void TimerElapsed(object state)
        {
            _onTextChangedDelayed(_textBox, EventArgs.Empty);
        }
    }

在表单构造函数中的用法:

InitializeComponent();
...
new DelayedText(txtEdit, txtEdit_OnTextChangedDelayed);

我还没有踢得很厉害,但似乎对我有用。

最直接的方法。

* .xaml

<TextBox Name="Textbox1"
             TextChanged="Textbox1_TextChanged"/>
* .xaml.cs

using System.Threading.Tasks;
public bool isChanging = false;
async private void Textbox1_TextChanged(object sender,
                                        TextChangedEventArgs e)
    {
        // entry flag
        if (isChanging)
        {
            return;
        }
        isChanging = true;
        await Task.Delay(500);
        // do your stuff here or call a function
        // exit flag
        isChanging = false;
    }

我有同样的问题,我认为最简单的解决方案是使用LostFocus事件:

xaml

<TextBox x:Name="YourTextBox" LostFocus="YourTextBox_LostFocus" />

xaml.cs

private void YourTextBox_LostFocus(object sender, RoutedEventArgs e)
{
    //Your code here
}

我想在Return/Tab和LostFocus上都提交一个文本框,所以我使用了这个复杂的解决方案,但它有效。

public static void TextBoxEditCommit(TextBox tb, Action<TextBox>OnEditCommit)
{
    if (OnEditCommit == null) 
            throw new ArgumentException("OnEditCommit delegate is mandatory.");
    //THis delegate fire the OnEditCommit Action
    EventHandler _OnEditCommit = delegate(object sender, EventArgs e) 
            { OnEditCommit(tb); };
    //Edit commit on Enter or Tab
    tb.KeyDown += delegate (object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab)
        {
            //Temporary remove lostfocus event for avoid double commits
            tb.LostFocus -= _OnEditCommit;
            OnEditCommit(tb);
            tb.LostFocus += _OnEditCommit;
        }
    };
    //Edit commit on LostFocus
    tb.LostFocus += _OnEditCommit;
}

你可以用下面的简单代码使用这个事件生成器:

//Check for valid content
UIUtil.TextBoxEditCommit(tbRuleName, (tb) => {
        //Your code here, tb.text is the value collected
            });