.NET --- Управление текстовым полем - подождите, пока пользователь закончит ввод

Приветствую всех,

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

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

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

Язык: VB.NET Framework:.Net 2.0

- Отредактировано, чтобы уточнить "закончил печатать"

6 ответов

Решение

Один подход:

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

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

  2. Во входных TextChanged событие, Stop() а потом Start() Timer

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

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

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

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

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

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

Для тех, кому нужно что-то подобное в.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); 
    }

}

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

Я добавил очень мало времени в Thread.Sleep, поскольку заметил, что метод invoke может произойти дважды, если пользователь печатает очень быстро, но с небольшой случайной задержкой между некоторыми нажатиями клавиш. Вы также должны добавить ссылку на сборку 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) Потерянный фокус

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

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

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

Глобальная декларация vb.net language

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

Подход, который я успешно использовал в прошлом, использует событие ручного сброса и асинхронные вызовы, чтобы определить, когда пользователь прекратил печатать. Код выглядит примерно так

// 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);
}
Другие вопросы по тегам