C# wait for user to finish typing in a Text Box

You can use textbox onChange() event. If text is changed in textbox, check if entered value is a number and calculate total value according to the other value.

I define "finished typing" now as "user has typed something but has not typed anything after a certain time". Having that as a definition i wrote a little class that derives from TextBox to extend it by a DelayedTextChanged event. I do not ensure that is complete and bug free but it satisfied a small smoke test. Feel free to change and/or use it. I called it MyTextBox cause i could not come up with a better name right now. You may use the DelayedTextChangedTimeout property to change the wait timeout. Default is 10000ms (= 10 seconds).

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

Another simple solution would be to add a timer to your form, set the Interval property to 250 and then use the timer's tick event as follows:

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

If you are using WPF and .NET 4.5 or later there is a new property on the Binding part of a control named "Delay". It defines a timespan after which the source is updated.

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

This means the source is updated only after 500 milliseconds. As far as I see it it does the update after typing in the TextBox ended. Btw. this property can be usefull in other scenarios as well, eg. ListBox etc.

You can handle the LostFocus event of the text box which will fire everytime the user finishes typing and navigates away from the text box. Here is the documentation on LostFocus: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.lostfocus.aspx

However, I am not sure what exactly you are trying to do here as the question is not very clear about what "finish" means.

I faced the same challenge, and here is my simple approach. This works without issues.

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

In UWP, I did a delayed check by making a static lastTimeOfTyping and checking the time when the "TextChanged" event happened. This waits till the static lastTimeOfTyping matches when a new "TextChanged" time matches and then executes the desired function.

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

You want to use handle either the Leave or LostFocus event for the textbox in question. I'm assuming you are using WinForm even though you don't state it in your question.

I don't know if the onChange() only exists in an older version of c#, but I can't find it!

The following works for detecting when a user either hits the Enter key, or tabs out of the TextBox, but only after changing some text:

    //--- 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  --- //

As an asynchronous extension method. Adapted from Grecon14's answer.

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

Usage:

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

Ideally an inheritance solution like esskar’s is the way to go but it doesn’t play well with the designer so to get the re-use I opted for a helper style 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);
        }
    }

Usage, in the form constructor:

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

I haven't kicked it hard, but seems to work for me.

What if you trigger an event based on a keystroke like tab or return?

A coworker of mine suggested a solution using Rx and event throttling:

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