2009-03-22 4 views
16

Salutations tous,.NET --- contrôle Textbox - attendre jusqu'à ce que l'utilisateur se fait taper

est-il construit dans le moyen de savoir quand un utilisateur se fait taper dans une zone de texte? (Avant de cliquer sur 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 décalage, car si un utilisateur tape rapidement dans la zone 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 de taper. Donc, s'ils tapent "a" et s'arrêtent, un événement se déclenche. Cependant, s'ils tapent «tout le chemin», l'événement se déclenche après le «y».

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

laissez-moi savoir ce que vous pensez.

Langue: VB.NET Framework: .Net 2.0

--Edited pour clarifier "fait taper"

Répondre

34

Une approche:

  1. Créer une Timer avec un Interval de millisecondes X

    L'intervalle devrait être d'environ 300 ms; plus d'un temps normal entre les frappes, et aussi un délai raisonnable d'attente entre la finition et la mise à jour se produisant

  2. En cas TextChanged de l'entrée, Stop() puis Start() la Timer

    Cela va redémarrer le Timer si elle est déjà en cours d'exécution, donc si l'utilisateur continue à taper à un taux normal, chaque changement redémarrera le minuteur.

  3. En cas Tick de la minuterie, Stop() la Timer et faire la longue transaction

  4. En option: Manipulez les Leave et KeyDown événements afin que laisser le contrôle ou en appuyant sur Entrée sera Stop() le Timer et faire long transaction.

Ceci provoquera une mise à jour si le texte a été modifié et que l'utilisateur n'a effectué aucune modification en X millisecondes. Un problème avec l'approche "Mesurer le temps écoulé depuis la dernière mise à jour" que vous envisagez est que si la dernière modification est effectuée rapidement, la mise à jour n'aura pas lieu, et il n'y aura pas de changements ultérieurs à déclencher un autre chèque.

Note: Il doit y avoir un pour un appariement entre TextBox es et Timer s; Si vous envisagez de le faire avec plus d'une entrée, je envisagerais de construire un UserControl qui encapsule cette fonctionnalité.

+0

À peu près l'approche que nous utilisons. –

+0

Bon plan, merci pour la suggestion. Donner la valeur ms est également très utile merci. –

+0

@Cj Anderson: Vous devrez peut-être jouer un peu pour trouver le bon équilibre entre la réactivité et la vitesse de frappe –

1

Cela dépend de ce que vous entendez par "fait taper." Il existe un événement pour vous informer lorsque l'utilisateur a quitté le focus de ce contrôle particulier. En outre, il y a même un changement qui vous indique quand le texte change. Qu'est-ce que vous pourriez faire est piège deux choses:

1) perdu le focus

2) Chaque fois que l'utilisateur modifie le texte, démarrer une minuterie pour dire, 20 secondes, et si l'utilisateur se fait dans ce délai, alors l'utilisateur est "fait" en tapant. C'est-à-dire si l'utilisateur n'a rien fait dans ce délai, alors supposez que l'utilisateur est "terminé".

Si l'une de ces deux choses se produit alors l'utilisateur est fait, assurez-vous d'arrêter et de redémarrer la minuterie de manière appropriée. Évidemment, vous pouvez changer le délai d'attente.

Tout dépend de la façon dont vous voulez le définir.

+0

Que signifie « et si l'utilisateur est fait dans ce temps "signifie? Qu'ils n'ont pas tapé de caractères supplémentaires? –

+0

Oui, désolé, j'ai édité le post pour refléter cela. – BobbyShaftoe

0

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 quand l'utilisateur a cessé de taper. Le code ressemble à 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); 
} 
3

J'ai fini par essayer réponse Scott Weinstein bien qu'il nécessitait des connaissances plus approfondies sur le threading, les délégués et la syntaxe lambda de base. Et ça a plutôt bien marché. Sa réponse originale n'était pas un copier-coller pur, je devais donc faire quelques efforts pour le faire fonctionner.

J'ai ajouté très peu de temps à Thread.Sleep, car j'ai remarqué que la méthode invoke peut arriver deux fois si l'utilisateur tape très rapidement mais avec un minuscule décalage aléatoire entre certaines frappes. Vous devez également ajouter une référence à l'assembly WindowsBase pour utiliser Dispatcher.

J'utilise 1,5 secondes pour attendre la fin de la saisie par l'utilisateur.

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

Pour ceux qui ont besoin de quelque chose comme ça dans .NET 2.0, ici, je fait un contrôle qui dérive de TextBox et utilise la même approche .. Hope this 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); 
    } 

} 
+0

Bonne solution. J'ai adaptateur votre code dans le mien. Étendu l'événement OnUserStopTyping et travaille maintenant pour moi. Merci de votre aide. –

Questions connexes