.NET --- Управление текстовым полем - подождите, пока пользователь закончит ввод
Приветствую всех,
Есть ли встроенный способ узнать, когда пользователь заканчивает ввод в текстовое поле? (Прежде чем нажать на вкладку, или переместить мышь) У меня есть запрос к базе данных, который происходит по событию с измененным текстом, и все работает отлично. Тем не менее, я заметил, что, конечно, есть небольшая задержка, потому что, если пользователь быстро вводит текстовое поле, программа занята выполнением запроса для каждого символа. Так что я надеялся, что это способ узнать, закончил ли пользователь печатать. Поэтому, если они набирают "а" и останавливаются, происходит событие. Однако, если они набирают "полностью", событие срабатывает после нажатия клавиши y.
У меня есть идеи, которые плавают у меня в голове, но я уверен, что они не самые эффективные. Как и измерение времени с момента последнего события textchange, и если оно будет> чем определенное значение, то оно продолжит выполнение остальных моих процедур.
дайте мне знать, что вы думаете.
Язык: VB.NET Framework:.Net 2.0
- Отредактировано, чтобы уточнить "закончил печатать"
6 ответов
Один подход:
Создать
Timer
сInterval
X миллисекундИнтервал должен быть около 300 мс; больше обычного времени между нажатиями клавиш, а также разумное время ожидания между завершением и обновлением
Во входных
TextChanged
событие,Stop()
а потомStart()
Timer
Это перезапустит
Timer
если он уже запущен, поэтому, если пользователь продолжает печатать с нормальной скоростью, каждое изменение перезапускает таймер.В таймер
Tick
событие,Stop()
Timer
и сделайте длинную сделкуДополнительно: обрабатывать
Leave
а такжеKeyDown
события, так что выход из управления или нажатие Enter будетStop()
Timer
и сделайте длинную сделку.
Это приведет к обновлению, если текст изменился, и пользователь не внес никаких изменений в X миллисекунд.
Одна из проблем, связанных с подходом "Измерение времени с момента последнего обновления", который вы рассматриваете, заключается в том, что если последнее изменение будет сделано быстро, обновление не произойдет, и не будет никаких последующих изменений, чтобы инициировать еще одну проверку.
Примечание. Должно быть соединение один к одному между TextBox
эс и Timer
s; если вы планируете делать это с более чем одним входом, я бы подумал о создании 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);
}