Подтвердить что ты не робот

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

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

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

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

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

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

- Отредактировано для уточнения "сделанного набора текста"

4b9b3361

Ответ 1

Один подход:

  • Создайте Timer с Interval из X миллисекунд

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

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

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

  • В событии таймера Tick Stop() Timer и выполните длинную транзакцию

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

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

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

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

Ответ 2

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

}

Ответ 3

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

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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