65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义文本框, 延迟 TextChanged 事件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (18投票s)

2007年8月17日

2分钟阅读

viewsIcon

99006

downloadIcon

3334

继承自基本文本框控件的文本框, 该控件会延迟文本更改事件

引言

虽然我浏览 The Code Project 已经有一段时间了,但这是我的第一篇文章,请多多包涵!

我在创建另一个控件,确切地说是一个分页器控件,并遇到一个问题。问题是当用户输入例如 100 来导航到该页面时,我捕获了 TextChanged 事件,它会先跳转到第 1 页,然后是第 10 页,最后是第 100 页(想象一下这个过程)。我不想发生这种情况,原因有几个,但主要是因为我没有将每个页面的数据存储在内存中,而是在从 SQL Server 选择页面时才填充数据,所以我希望消除 3 次查询,而对于前面提到的例子,只需要 1 次查询。

因此,我决定不要向我的分页器控件添加“转到”或“刷新”按钮,而是延迟 TextBox TextChanged 事件,这样只要用户正在输入,事件就不会触发,直到他/她完成输入(达到延迟阈值)。

希望对某人有用!

使用控件

使用该控件很简单,因为除了 TextChanged 事件触发的时间之外,TextBox 没有什么不同。唯一添加的属性是延迟,您可以像这样设置它,假设您已经将控件添加到您的窗体中。

// Delay in number of milliseconds, the delay gets reset after a key press

// if it hasn't already elapsed

delTextBox1.Delay = 1000;
delTextBox1.TextChanged += new EventHandler(delTextBox1_TextChanged);

解释源代码

只有几个主要部分,但对于初学者来说可能有些令人困惑,所以我将尽力解释它们。

首先要创建的是继承基础 TextBox 控件的新控件,添加计时器和延迟属性。计时器将用于控制稍后讨论的 TextChanged 事件。

...
using System.Timers;

namespace MyControl
{
     public class DelayTextBox : TextBox
     {
           System.Timers.Timer delayTimer;
       private int _threshold = 1000;   // default it 1 second



       // Delay Property to set the amount of time the control

       // waits before firing the TextChanged Event.

           public int Delay
           {
        set{ _threshold = value; }
           }

       public DelayTextBox()
       {
        delayTimer = new System.Timers.Timer( _threshold );
           }
     }
}

接下来,我重写了 KeyPress 事件,我需要这个事件来指示 TextChanged 事件,有内容正在输入到 TextBox 中。

private bool KeysPressed = false;

protected override void OnKeyPress(KeyPressEventArgs e)
{

   // check if timer has been started, if not start

   // if it has, restart it.

   if (!DelayTimer.Enabled)
   {
      DelayTimer.Enabled = true;
   }
   else
   {
      DelayTimer.Enabled = false;
      DelayTimer.Enabled = true;
   }

   // used to indicate the text was changed because a key was pressed

   // and not just setting the text property.

   KeysPressed = true;

   // execute base code as usual

   base.OnKeyPress(e);
}

现在我们需要重写 TextChanged 事件,以便在每次实际更改时阻止它触发,直到计时器到期。

// set to true in the Timer_Elapsed event.

private bool TimerElapsed = false;

protected override void OnTextChanged(EventArgs e)
{
   // if the timer elapsed or text was changed by something 

   // besides a keystroke

   // fire base.OnTextChanged

   if ( TimerElapsed || !KeysPressed )
   {
       TimerElapsed = false;  // reset

       KeysPressed = false;   // reset

       base.OnTextChanged(e); // run base TextChanged event code.

   }
}

最后是 Timer_Elapsed 事件的代码。这部分比其他部分稍微复杂一些,因为 Timer 在自己的线程中运行,所以使用 Invoke 方法来使事件触发是必需的,否则它会在您的父窗体或控件中导致问题。

public delegate void DelayOverHandler();

public void DelayOver()
{
    // call on textchanged.

    OnTextChanged(new EventArgs());
}

void delayTimer_Elapsed(object sender, EventArgs e)
{
    // stop timer.

    DelayTimer.Enabled = false;

    // set TimerElapsed to true, so the OnTextChange executes its base code.

    TimerElapsed = true;

    // use invoke to get back on the UI thread.

    this.Invoke(new DelayOverHandler(DelayOver), null);
}

这个控件似乎符合我的需求,我希望有人可以用它来做一些事情。我计划添加一个键按下过滤器,只允许数字,但这里有很多文章可以完成这个任务。

历史

  • 2007 年 8 月 16 日:发布原始文章
© . All rights reserved.