.NET --- 文本框控件 - 等待用户输入完成

16

大家好,

是否有内置的方法可以知道用户何时完成文本框内的输入?(在按下 Tab 键或移动鼠标之前)我有一个在 textchanged 事件上执行的数据库查询,一切正常。然而,我注意到当用户快速输入文本框时,会有一些延迟,因为程序正在为每个字符进行查询。因此,我希望能够找到一种方式来判断用户是否已经完成了输入。例如,如果他们键入 "a" 并停止,那么将触发一个事件。但是,如果他们键入 "all the way",则事件会在松开 y 键后触发。

我脑中有一些想法,但我确信它们不是最有效的。例如,测量自上次 textchanged 事件以来的时间,如果大于某个值,则执行其余的过程。

请告诉我您的想法。

语言:VB.NET 框架:.Net 2.0

--编辑以澄清“完成输入”

6个回答

34

一种方法:

  1. 创建一个定时器 Timer,间隔为 X 毫秒

    间隔应该大约为 300 毫秒;比正常按键之间的时间长,但也是等待结束和更新发生之间合理的时间。

  2. 在输入框的 TextChanged 事件中,先 Stop()Start() 定时器 Timer

    如果定时器已经在运行,则这将重新启动定时器,因此如果用户以正常速率继续打字,则每个更改都会重新启动定时器。

  3. 在定时器的 Tick 事件中,Stop() 定时器,并执行耗时的操作

  4. 可选:处理 LeaveKeyDown 事件,以便离开控件或按下 EnterStop() 定时器并执行耗时的操作。

这将导致在文本更改且用户 X 毫秒没有进行任何更改时进行更新。

你正在考虑的"测量自上次更新以来的时间"方法的一个问题是,如果最后一次更改很快,更新将不会发生,也没有任何后续更改来触发另一个检查。

注意:TextBoxTimer 必须一一配对;如果您计划在多个输入框中使用此功能,建议构建包装此功能的 UserControl


我们基本上采用的方法。 - Jeff Yates
好的计划,谢谢你的建议。提供ms值也非常有帮助,谢谢。 - Cj Anderson
@Cj Anderson:你可能需要花点时间来调整它,以找到响应速度和打字速度之间的平衡。 - Daniel LeCheminant
1
我很感激你提供了一个好的但是非常难以理解的解决方案。如果你提供示例代码来更容易地理解它,那不是更好吗? - kashif

5

对于那些需要在.NET 2.0中使用类似功能的人,我制作了一个继承自TextBox并使用相同方法的控件。 希望这能够帮助到你。

public partial class TextBox : System.Windows.Forms.TextBox
{

    private ManualResetEvent _delayMSE;
    public event EventHandler OnUserStopTyping;
    private delegate bool TestTimeout();

    public TextBox()
    {
        _delayMSE = new ManualResetEvent(false);
        this.TextChanged += new EventHandler(TextBox_TextChanged);
    }

    void TextBox_TextChanged(object sender, EventArgs e)
    {


        _delayMSE.Set();
        Thread.Sleep(20);
        _delayMSE.Reset();

        TestTimeout tester = new TestTimeout(TBDelay);
        tester.BeginInvoke(new AsyncCallback(Test), tester);

    }


    private void Test(IAsyncResult pResult)
    { 
        bool timedOut = (bool)((TestTimeout)pResult.AsyncState).EndInvoke(pResult);
        if (timedOut)
        {
            if (OnUserStopTyping != null)
                OnUserStopTyping(this, null);
        }
    }

    private bool TBDelay()
    { 
        return !_delayMSE.WaitOne(500, false); 
    }

}

好的解决方案。我已经将你的代码适配到我的项目中了。扩展了OnUserStopTyping事件,现在它对我很有用。感谢你的帮助。 - Eduardo Xavier

3

尽管它需要一些关于线程、委托和基本 lambda 语法的更深层次的知识,但我最终尝试了 Scott Weinstein 的答案。它运行得非常好。他的原始答案不是简单的复制粘贴,所以我需要做一些调整来让它可以工作。

由于我注意到当用户快速输入时,调用方法可能会发生两次,但某些按键之间有微小的随机延迟,所以我只在 Thread.Sleep 中添加了很少的时间。您还必须添加对 WindowsBase 组件的引用才能使用 Dispatcher。

我等待用户结束打字的时间是 1.5 秒。

    // use manual reset event to Q up waiting threads.
    // each new text changed event clears the Q
    // only the last changed will hit the timeout, triggering the action
    private ManualResetEvent _delayMSE;
    private Func<bool> TBDelay;
    private delegate void ActionToRunWhenUserStopstyping();

    public Form1()
    {
        InitializeComponent();

        _delayMSE = new ManualResetEvent(false);
        TBDelay = () => !_delayMSE.WaitOne(1500, false);
    }

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        _delayMSE.Set(); 

        // open the ResetEvent gate, to discard these delays    
        Thread.Sleep(20);
        // let all pending through the gate    
        _delayMSE.Reset();
        // close the gate
        TBDelay.BeginInvoke(res =>    
        {        
            // callback code        
            // check how we exited, via timeout or signal.        
            bool timedOut = TBDelay.EndInvoke(res);
            if (timedOut)
                Dispatcher.CurrentDispatcher.Invoke(
                    new ActionToRunWhenUserStopstyping(DoWhatEverYouNeed), 
                    DispatcherPriority.Input);
        }, null);
    }

    private void DoWhatEverYouNeed()
    {
        MessageBox.Show(textBox1.Text);
    }

1

这取决于你所说的“完成打字”的定义。有一个事件可以让你知道用户何时离开了特定控件的焦点。此外,还有一个更改事件可以告诉你文本何时发生了更改。你可以捕获两个事情:

1)失去焦点

2)每当用户更改文本时,启动一个计时器,比如20秒,如果用户在那段时间内完成,则用户已经“完成”打字。如果用户在那段时间内没有做任何事情,则假定用户已经“完成”。

如果发生这两种情况之一,那么用户就完成了,确保适当地停止和重新启动计时器。显然,你可以更改超时时间。

这完全取决于你想如何定义它。


“and if the user is done within that time” 是什么意思?这是否意味着他们没有输入任何其他字符? - Daniel LeCheminant

0

'vb.net语言 '全局声明

Public Tawal as integer 

'关于 Timer1 属性

Timer1.enable = true
Timer1.inverval =1000

'在程序中

Timer1.start()
if Tawal <2 then T awal =0
if Tawal >3 then 
   Call FillGrid
   Timer1.stop()
end if

0
过去我成功使用的方法是使用手动重置事件和异步调用来检测用户何时停止键入。 代码大致如下:
// use manual reset event to Q up waiting threads.
// each new text changed event clears the Q
// only the last changed will hit the timeout, triggering the action
private ManualResetEvent _delayMSE;
private Func<bool> TBDelay = () => !_delayMSE.WaitOne(600, false);
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    SendOrPostCallback ActionToRunWhenUserStopsTyping = o =>
    {
        // ...
    };

    _delayMSE.Set(); // open the ResetEvent gate, to discard these delays
    Thread.Sleep(0); // let all pending through the gate
    _delaySearchMSE.Reset(); // close the gate
    TBDelay.BeginInvoke(res =>
    {
        // callback code
        // check how we exited, via timeout or signal.
        bool timedOut = TBDelay.EndInvoke(res);
        if (timedOut)
            Dispatcher.Invoke(DispatcherPriority.Input, 
                            ActionToRunWhenUserStopstyping,null);
    }, null);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接