Я уверен, многим из Вас приходилось сталкиваться с необходимостью фильтровать данные по временному окну. Для этого на форме размещают пару стандартных DateTimePicker’ов и используют событие ValueChanged для получения данных в выбранном промежутке. Но как только Вы прервете работу обработчика этого события, не дав ему завершиться (сообщением об ошибке или окном отображающем процесс получения данных), Вы столкнетесь, нет, не с ошибкой или глюком, а с особенностью поведения календаря DateTimePicker’а – залипанием.
Для того что бы воспроизвести эту особенность, попробуйте расположить на форме DateTimePicker и написать вот такой обработчик события ValueChanged:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
MessageBox.Show();
}
Теперь попробуйте изменить период в «календаре»…
Попробовали?
Если же Вам было лень вопроизводить этот пример, то я раскажу в кратце, что вы могли бы увидеть.
В результате этого эксперимента контрол DateTimePicker переходит в режим «залипания». То есть это вполне запланированное поведение, когда при длительном удержании кнопки с выбором месяца, месяца последовательно изменяются. Но мы же вовсе не этого добивались! И гневное недовольство пользователей нас совсем не обрадует...
Поэтому мы начали разбираться, что к чему.
Во-первых, мы поняли что, необходимо разрешить обработчику события ValueChanged завершиться до того как мы заблокируем основной поток.
Во-вторых, все-таки нам необходимо заблокировать основной поток, что бы пользователи не смогли изменить свой выбор, пока мы получаем данные.
Не зря говорят: “Правильно заданный вопрос - это уже половина ответа”.
Первую проблему можно решить, запустив таймер или просто еще однин поток (мы выбрали простой поток).
Например, вот так:
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
// Создаем поток чтобы дать завершиться обработчику события ValueChanged
Thread tr = new Thread(ShowMess);
tr.IsBackground = true;
tr.Start();
}
void ShowMessThread()
{
this.Invoke(new runmess(ShowMess));
}
void ShowMess()
{
MessageBox.Show("test");
}
Теперь эффекта залипания нет, но мы можем беспрепятственно получить доступ к главной форме, так как наше сообщение запущенно в отдельном потоке.
Вторая проблема решается вызовом через Invoke метода обновления из основного потока.
private delegate void runmess();
private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
MessageBox.Show("test");
// Создаем поток чтобы дать завершиться обработчику события ValueChanged
Thread tr = new Thread(ShowMessThread);
tr.IsBackground = true;
tr.Start();
}
void ShowMessThread()
{
this.Invoke(new runmess(ShowMess));
}
void ShowMess()
{
MessageBox.Show("test");
}
Таким образом, мы даем возможность завершиться обработчику события ValueChanged, что не приводит к залипанию DateTimePicker’а, и блокируем основной поток для того, что бы пользователь не мог изменить свой выбор до завершения процесса получения данных или просто закрытия формы с сообщением.