ananda ananda - 3 months ago 29
C# Question

Don't raise TextChanged while continuous typing

I have a textbox that has a fairly hefty

_TextChanged
event handler. Under normal typing condition the performance is okay, but it can noticeably lag when the user performs a long continuous action, such as keeping the backspace button pressed to delete a lot of text at once.

For example, the event took 0.2 seconds to complete, but the user is performing one deletion every 0.1 seconds. Thus, it cannot catch up and there will be a backlog of events that needs to be handled, leading to the UI lagging.

However, the event does not need to run for these in-between states, because it only cares about the end result. Is there any way to let the event handler know that it should process only the latest event, and ignore all the previous stale changes?

Answer

I've come across this problem several times and found this solution simple and neat so far. It is working in Windows Form but can be easily converted to WPF.

Explanation:

When an object of TypeAssistant is informed that a text change happened, it starts a timer. After passing WaitingMilliSeconds, the timer raises Idle event. By handling this event you can do your job. If in the meantime another text change occurs, the timer resets.

public class TypeAssistant
{
    public event EventHandler Idled = delegate { };
    public int WaitingMilliSeconds { get; set; }
    System.Threading.Timer waitingTimer;

    public TypeAssistant(int waitingMilliSeconds = 600)
    {
        WaitingMilliSeconds = waitingMilliSeconds;
        waitingTimer = new Timer(p =>
        {
            Idled(this, EventArgs.Empty);
        });
    }
    public void TextChanged()
    {
        waitingTimer.Change(WaitingMilliSeconds, System.Threading.Timeout.Infinite);
    }
}

Usage:

public partial class Form1 : Form
{
    TypeAssistant assistant;
    public Form1()
    {
        InitializeComponent();
        assitant = new TypeAssistant();
        assitant.Idled += assitant_Idled;          
    }

    void assistant_Idled(object sender, EventArgs e)
    {
        this.Invoke(
        new MethodInvoker(() =>
        {
            // do your job here
        }));
    }

    private void yourFastReactingTextBox_TextChanged(object sender, EventArgs e)
    {
        assistant.TextChanged();
    }
}

Advantages:

  • Simple!
  • Working on WPF and Windows Form
  • Working on .Net Framework 3.5+

Disadvantages:

  • Running another thread
  • Needs Invokation instead of direct manipulating of the form controls
Comments