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






4.79/5 (18投票s)
2007年8月17日
2分钟阅读

99006

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 日:发布原始文章