Из стр. 108 из Шаблоны проектирования: Элементы многоразового объектно-ориентированного программного обеспечения Гамма, Шлем, Джонсон и Влиссидес.
Используйте шаблон Factory Method, когда
Один подход:
Создают Timer
с Interval
из X миллисекунд
, интервал должен составить приблизительно 300 мс; больше, чем нормальное время между нажатиями клавиш, и также разумный срок для ожидания между окончанием и обновлением, происходящим
Во входе TextChanged
событие Stop()
и затем Start()
Timer
, Это перезапустит Timer
, если это будет уже работать, поэтому если пользователь будет продолжать вводить при нормальном темпе, то каждое изменение перезапустит таймер.
В таймере Tick
событие, Stop()
Timer
и делают длинную транзакцию
Дополнительный: Обработайте Leave
и KeyDown
события так, чтобы выходить из-под контроля или нажатие Вошли будет Stop()
Timer
и делают длинную транзакцию.
Это вызовет обновление, если текст изменился, и пользователь не внес изменений в X миллисекундах.
Одна проблема с "Мерой время начиная с последнего обновления" подход, который Вы рассматриваете, - то, что, если последнее изменение внесено быстро, обновления не произойдет, и не будет никаких последующих изменений для инициирования другой проверки.
Примечание: должен быть один к одному соединению между [1 114] es и Timer
с; если бы Вы - планирование выполнения этого больше чем с одним входом, я рассмотрел бы создание UserControl
, который обертывает эту функциональность.
Это зависит от того, что Вы подразумеваете "под сделанным вводом". Существует событие для сообщения, когда пользователь оставил фокус того конкретного управления. Кроме того, существует измененный даже, который говорит Вам, когда текст изменяется. То, что Вы могли сделать, захватить две вещи:
1) Потерянный фокус
2) Каждый раз пользователь изменяет текст, запустите, таймер для говорят, 20 секунд, и если пользователь сделан в течение того времени, то пользователь "сделан", введя. Это - то, если пользователь ничего не сделал в течение того времени, тогда предполагают, что пользователь "сделан".
, Если любая из тех двух вещей происходит тогда, пользователь сделан, удостоверьтесь, что остановили и перезапустили таймер соответственно. Очевидно, Вы могли изменить тайм-аут.
Все это зависит от того, как Вы хотите определить его.
Подход, который я использовал успешно в прошлом использовании ручное событие сброса и асинхронные вызовы для обнаружения, когда пользователь прекратил вводить. Код выглядит примерно так
// 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);
}
В итоге я попробовал ответить Скотта Вайнштейна, хотя для этого потребовались некоторые более глубокие знания о потоках, делегатах и базовом синтаксисе лямбда. И это сработало очень хорошо. Его первоначальный ответ не был чистым копипастом, поэтому мне пришлось немного поиграться, чтобы заставить его работать.
Я добавил очень мало времени в Thread.Sleep, так как заметил, что метод вызова может сработать дважды, если пользователь набирает текст очень быстро. но с крошечной случайной задержкой между некоторыми нажатиями клавиш. Вы также должны добавить ссылку на сборку 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);
}
Для тех, кому нужно что-то подобное в .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);
}
}