2009-03-22 1 views
16

Привет всем,.NET --- управления Textbox - ждать, пока пользователь сделал набрав

Есть встроенный способ знать, когда пользователь сделал вводить в текстовое поле? (Перед удалением вкладки, или перемещением мыши) У меня есть запрос к базе данных, который встречается на событии textchanged, и все работает отлично. Однако я заметил, что есть немного отставания, потому что, если пользователь быстро вводит текстовое поле, программа занята выполнением запроса для каждого символа. Так что я надеялся, это способ узнать, закончил ли пользователь печатать. Поэтому, если они набирают «a» и останавливаются, происходит событие. Однако, если они набирают «весь путь», событие запускается после нажатия клавиши y.

У меня есть идеи, плавающие вокруг моей головы, но я уверен, что они не самые эффективные. Как и измерение времени с момента последнего события обмена текстовыми сообщениями, и если оно было> чем определенное значение, оно будет продолжать выполнять остальные мои процедуры.

дайте мне знать, что вы думаете.

Язык: .Net 2.0

--Edited уточнить "сделано набрав"

ответ

34

Один из подходов:

  1. Создать Timer с Interval Х миллисекунд

    интервал должен быть около 300 мс; больше, чем обычное время между нажатиями клавиш, а также разумное время ожидания между отделкой и обновлением, произошедшим

  2. В TextChanged случае сторон исходного, Stop(), а затем Start()Timer

    Это приведет к перезапуску Timer, если она уже поэтому, если пользователь продолжает печатать с обычной скоростью, каждое изменение перезапускает таймер.

  3. В Tick событии таймера, Stop()Timer и сделать длинную сделку

  4. дополнительно: Ручка с Leave и KeyDown события так, чтобы оставить контроль или нажав Enter будет Stop() в Timer и сделать длинный сделка.

Это приведет к обновлению, если текст изменился, и пользователь не внес никаких изменений в течение X миллисекунд.

Одна из проблем с подходом «Измерение времени с момента последнего обновления», который вы рассматриваете, заключается в том, что если последнее изменение выполняется быстро, обновление не произойдет, и никаких последующих изменений для запуска не произойдет другой проверка.

Примечание: Там должен быть один к одному спариванию между TextBox эсами и Timer с; если вы планируете делать это с несколькими входами, я бы подумал о создании UserControl, который обертывает эту функциональность.

+0

Довольно подход, который мы используем. –

+0

Хороший план, спасибо за предложение. Давать значение ms также очень полезно. –

+0

@Cj Anderson: Вам, возможно, придется немного поиграть с ним, чтобы найти правильный баланс между скоростью реагирования и скоростью печати –

1

Это зависит от того, что вы имеете в виду: VB.NET Framework "Done печатать." Есть событие, которое позволит вам узнать, когда пользователь оставил фокус этого конкретного элемента управления. Кроме того, есть изменения, которые даже говорят о том, когда текст меняется. То, что вы можете сделать, это ловушка две вещи:

1) Затерянный фокус

2) Каждый раз, когда пользователь изменяет текст, начать отсчет времени, скажем, 20 секунд, и если пользователь будет сделано в течение этого времени, затем пользователь «набирает». То есть, если пользователь ничего не сделал в течение этого времени, тогда предположите, что пользователь «сделан».

Если какая-либо из этих двух вещей происходит, то пользователь выполнен, обязательно остановите и перезапустите таймер соответствующим образом. Очевидно, вы можете изменить таймаут.

Все зависит от того, как вы хотите определить его.

+0

Что делает «и если пользователь в это время "означает? Что они не набрали никаких дополнительных символов? –

+0

Да, извините, я отредактировал сообщение, чтобы это отразить. – BobbyShaftoe

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

Я в конечном итоге пытается Скотт Вайнштейн ответ, хотя он требует более глубоких знаний о резьбе, делегатов и основной синтаксис лямбда. И это сработало очень хорошо. Его оригинальный ответ был не чистым копированием, поэтому мне пришлось немного поиграть, чтобы заставить его работать.

Я добавил очень мало времени для Thread.Sleep, так как я заметил, что метод вызова может произойти дважды, если пользователь печатает очень быстро, но с небольшой случайной задержкой между некоторыми нажатиями клавиш. Вы также должны добавить ссылку на сборку WindowsBase для использования диспетчера.

Я использую 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); 
    } 
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); 
    } 

} 
+0

Хорошее решение. Я адаптирую ваш код в свою. Расширено событие OnUserStopTyping и теперь работает для меня. Спасибо за вашу помощь. –