2009-03-22 26 views
16

Saludos a todos,.NET --- control de cuadro de texto - esperar hasta que el usuario se realiza escribiendo

¿Hay una construida en la forma de saber cuándo un usuario se realiza escribiendo en un cuadro de texto? (Antes de presionar la pestaña, O moviendo el mouse) Tengo una consulta de base de datos que ocurre en el evento textchanged y todo funciona perfectamente. Sin embargo, noté que hay un poco de retraso, por supuesto, porque si un usuario está escribiendo rápidamente en el cuadro de texto, el programa está ocupado haciendo una consulta para cada personaje. Entonces, lo que esperaba era una forma de ver si el usuario había terminado de escribir. Entonces, si escriben "a" y se detienen, se desencadena un evento. Sin embargo, si escriben "todo el camino", el evento se activa después de la tecla y.

Tengo algunas ideas flotando en mi cabeza, pero estoy seguro de que no son las más eficientes. Al igual que medir el tiempo transcurrido desde el último evento de cambio de texto y si era> mayor que cierto valor, entonces continuaría ejecutando el resto de mis procedimientos.

déjame saber lo que piensas.

Idioma: .Net 2.0

--Edited para aclarar "hecho escribiendo"

Respuesta

34

Un enfoque:

  1. Crear una Timer con un Interval de milisegundos X

    El intervalo debe ser de aproximadamente 300 ms; más de un tiempo normal entre las pulsaciones del teclado, y también tiempo razonable de espera entre el acabado y la actualización se produce

  2. En el evento de la entrada TextChanged, Stop() y luego Start() la Timer

    Esto reiniciará el Timer si ya está funcionando, entonces si el usuario sigue escribiendo a una velocidad normal, cada cambio reiniciará el temporizador.

  3. En caso de que el temporizador Tick, Stop() la Timer y hacer la transacción larga

  4. Opcional: Manejar los Leave y KeyDown eventos para que deje el mando o pulsando Introduzca se Stop() la Timer y hacer el largo transacción.

Esto hará que una actualización si el texto ha cambiado, y el usuario no ha realizado ningún cambio en X milisegundos.

Un problema con el enfoque de "Medir el tiempo desde la última actualización" que está considerando es que si el último cambio se realiza rápidamente, la actualización no se realizará y no habrá ningún cambio posterior para activar otro cheque

Nota: Debe haber uno a uno emparejamiento entre TextBox es Timer y s; Si planea hacer esto con más de una entrada, consideraría construir un UserControl que envuelva esta funcionalidad.

+0

Más o menos el enfoque que usamos. –

+0

Buen plan, gracias por la sugerencia. Dar el valor ms también es bastante útil gracias. –

+0

@Cj Anderson: Puede que tengas que jugar un poco para encontrar el equilibrio adecuado entre la capacidad de respuesta y las velocidades de escritura –

1

Depende de lo que entendemos por: VB.NET marco de "escribir hecho". Hay un evento que le permite saber cuándo el usuario dejó el foco de ese control en particular. Además, hay un cambio que incluso te dice cuándo cambia el texto. Lo que podría hacer es trampa de dos cosas:

1) foco perdida

2) Cada vez que el usuario cambia el texto, se inicia un temporizador para decir, 20 segundos, y si el usuario se realiza dentro de ese tiempo, entonces el usuario está "listo" para escribir. Es decir, si el usuario no ha hecho nada dentro de ese tiempo, suponga que el usuario está "hecho".

Si ocurre cualquiera de estas dos cosas, entonces el usuario ha terminado, asegúrese de detener y reiniciar el temporizador de manera apropiada. Obviamente, podrías cambiar el tiempo de espera.

Todo depende de cómo quiera definirlo.

+0

¿Qué significa "y si el usuario está hecho dentro de ese tiempo "¿significa? ¿Que no han tecleado ningún personaje adicional? –

+0

Sí, lo siento, edité la publicación para reflejar esto. – BobbyShaftoe

0

El enfoque que he utilizado con éxito en el pasado usa un evento de restablecimiento manual y invocaciones asincrónicas para detectar cuándo el usuario ha dejado de escribir. El código es algo como esto

// 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

terminé tratando respuesta de Scott Weinstein aunque requiere algún conocimiento más profundo acerca de rosca, los delegados y la sintaxis básica lambda. Y funcionó bastante bien. Su respuesta original no era pura copia y pega, así que tuve que jugar un poco para que funcionara.

Agregué muy poco tiempo a Thread.Sleep, ya que noté que el método de invocación puede suceder dos veces si el usuario está escribiendo muy rápido, pero con un pequeño retraso aleatorio entre algunas teclas. También debe agregar una referencia al ensamblaje de WindowsBase para usar Dispatcher.

Uso 1,5 segundos para esperar a que el usuario termine de escribir.

// 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

Para aquellos que necesitan algo como esto en .NET 2.0, aquí hice un control que se deriva del cuadro de texto y utiliza el mismo enfoque .. Esperanza esta ayuda

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

Buena solución. He adaptado tu código al mío. Extendí el evento OnUserStopTyping y ahora está funcionando para mí. Gracias por tu ayuda. –

Cuestiones relacionadas