.net-Textbox control-attendre que l'utilisateur ait terminé de taper

Salutations à tous,

est-ce qu'il y a une façon de savoir quand un utilisateur a fini de taper dans une boîte de texte? (Avant de frapper l'onglet, ou de déplacer la souris) j'ai une requête de base de données qui se produit sur l'événement textchanged et tout fonctionne parfaitement. Cependant, j'ai remarqué qu'il y a un peu de retard bien sûr parce que si un utilisateur tape rapidement dans la boîte de texte le programme est occupé à faire une requête pour chaque caractère. Donc, ce que j'espérais était un moyen de voir si l'utilisateur a fini d'écrire. Si s'ils tapent "a" et arrêtent alors un événement se déclenche. Cependant, s'ils tapent "all the way", l'événement se déclenche après le Y keyup.

j'ai quelques idées qui flottent autour de ma tête mais je suis sûr qu'elles ne sont pas les plus efficaces. Comme mesurer le temps écoulé depuis le dernier événement textchange et s'il était > qu'une certaine valeur alors il procéderait à exécuter le reste de mes procédures.

dites-moi ce que vous en pensez.

langue: VB.NET Cadre: .Net 2.0

--Édité pour clarifier "fait de taper"

16
demandé sur Cj Anderson 2009-03-23 01:40:22

6 réponses

Une approche:

  1. Créer un Timer avec un Interval DE X millisecondes

    l'intervalle devrait être d'environ 300ms; plus qu'un temps normal entre les frappes, et aussi un temps raisonnable pour attendre entre la fin et la mise à jour

  2. Dans l'entrée TextChanged événement Stop() et Start()Timer

    cela va relancer le Timer si elle est déjà en cours d'exécution, alors si l'utilisateur garde en tapant à une vitesse normale, chaque changement redémarre le minuteur.

  3. Dans le timer Tick événement Stop()Timer et faire la transaction longue

  4. en Option: Poignée de l' Leave et KeyDown événements de sorte qu'en quittant la contrôle ou en appuyant sur EntréeStop()Timer et faire la transaction longue.

cela provoquera une mise à jour si le texte a changé, et que l'utilisateur n'a pas fait tout changement en X millisecondes.

un problème avec l'approche "Measure the time since the last update" que vous envisagez est que si le dernier changement est fait rapidement, la mise à jour n'aura pas lieu, et il n'y aura pas de changements ultérieurs pour déclencher une autre vérification.

Remarque: Il doit y avoir un couplage entre TextBoxes et Timers; si vous avez l'intention de le faire avec plus d'une entrée, j'avais envisager la construction d'un UserControl qui entoure cette fonctionnalité.

34
répondu Daniel LeCheminant 2009-03-22 23:35:40

pour ceux qui ont besoin de quelque chose comme ça dans .NET 2.0, ici j'ai fait un contrôle qui dérive de TextBox et utilise la même approche.. Espérons que cette aide

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

}
5
répondu Diego Carballo 2010-05-05 22:51:27

J'ai fini par essayer la réponse de Scott Weinstein bien que cela nécessitait des connaissances plus approfondies sur le threading, les délégués et la syntaxe lambda de base. Et cela a fonctionné assez bien. Sa réponse originale n'était pas du pur copier-coller, donc j'ai dû jouer pour que ça marche.

j'ai ajouté très peu de temps au Thread.Dormir, depuis que j'ai remarqué la méthode invoke peut se produire deux fois si l'utilisateur tape vraiment rapidement, mais avec un petit délai aléatoire entre certaines des touches. Vous devez également ajouter une référence à Montage de la base de fenêtre pour L'utilisation du répartiteur.

j'utilise 1,5 secondes pour attendre que l'utilisateur tape.

    // 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);
    }
3
répondu Clack 2009-07-23 16:07:28

cela dépend de ce que vous entendez par "fait de taper."Il y a un événement pour vous faire savoir quand l'Utilisateur a quitté le foyer de ce contrôle particulier. De plus, il y a un changement même qui vous indique quand le texte change. Ce que vous pourriez faire, c'est piéger deux choses:

1) le foyer Perdu

2) Chaque fois que l'utilisateur modifie le texte, démarrer un compte à rebours de 20 secondes, et si l'utilisateur est effectué dans ce délai, l'utilisateur est "fait" taper. C'est-à-dire si l'utilisateur n'a rien fait ce temps suppose alors que l'utilisateur est " fait."

si l'un ou l'autre de ces deux événements se produit alors que l'utilisateur est fait, assurez-vous d'arrêter et de redémarrer le minuteur de manière appropriée. Évidemment, vous pouvez modifier le délai d'attente.

tout dépend de la façon dont vous souhaitez définir.

1
répondu BobbyShaftoe 2009-03-22 23:56:03

l'approche que j'ai utilisée avec succès dans le passé utilise un événement de réinitialisation manuelle et des invocations asynchrones pour détecter lorsque l'Utilisateur a cessé de taper. Le code ressemble à quelque chose comme ceci

// 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);
}
0
répondu Scott Weinstein 2009-03-22 23:18:02