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

Привет всем,

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

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

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

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

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

Ответы [ 6 ]

34 голосов
/ 23 марта 2009

Один подход:

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

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

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

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

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

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

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

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

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

5 голосов
/ 06 мая 2010

Для тех, кому нужно что-то подобное в .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 голосов
/ 23 июля 2009

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

Я добавил очень мало времени в 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 голос
/ 23 марта 2009

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

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

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

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

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

0 голосов
/ 14 июля 2018

'язык vb.net 'глобальная декларация

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
0 голосов
/ 23 марта 2009

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

// 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...