В приложении WPF я использую BackgroundWorker для периодической проверки на условие на сервере. В то время как это хорошо работает, я хочу вытолкать MessageBox notifing пользователи, если что-то перестало работать во время проверки.
Вот то, что я имею:
public static void StartWorker()
{
worker = new BackgroundWorker();
worker.DoWork += DoSomeWork;
worker.RunWorkerAsync();
}
private static void DoSomeWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
Thread.Sleep(5000);
var isOkay = CheckCondition();
if(!isOkay)
MessageBox.Show("I should block the main window");
}
}
Но этот MessageBox не блокирует главное окно. Я могу все еще нажать на свое приложение WPF и изменить что-либо, что я люблю с MessageBox вокруг.
Как я решаю это?Спасибо,
Править:
Для ссылки это - то, что я закончил тем, что делал:
public static void StartWorker()
{
worker = new BackgroundWorker();
worker.DoWork += DoSomeWork;
worker.ProgressChanged += ShowWarning;
worker.RunWorkerAsync();
}
private static void DoSomeWork(object sender, DoWorkEventArgs e)
{
while (!worker.CancellationPending)
{
Thread.Sleep(5000);
var isOkay = CheckCondition();
if(!isOkay)
worker.ReportProgress(1);
}
}
private static void ShowWarning(object sender, ProgressChangedEventArgs e)
{
MessageBox.Show("I block the main window");
}
Вызовите ReportProgress
и передайте this
в MessageBox.Show
.
Он не только не блокирует главное окно, но и с большой вероятностью исчезнет за ним. Это прямое следствие того, что оно работает в другом потоке. Если вы не указали владельца для окна сообщения, то оно ищет его с помощью API-функции GetActiveWindow(). Она учитывает только те окна, которые используют одну и ту же очередь сообщений. Что является свойством, специфичным для конкретного потока. Потерять окно сообщений, конечно, довольно сложно.
Аналогично, MessageBox блокирует только те окна, которые принадлежат одной и той же очереди сообщений. И, таким образом, не блокирует окна, созданные вашим главным потоком.
Решите проблему, позволив потоку пользовательского интерфейса отображать окно сообщения. Используйте Dispatcher.Invoke или задействуйте события ReportProgress или RunWorkerCompleted. Не похоже, что события здесь уместны.
Замените
MessageBox.Show("I should block the main window");
на
this.Invoke((Func<DialogResult>)(() => MessageBox.Show("I should block the main window")));
, что приведет к тому, что окно сообщения будет находиться в основном потоке и заблокирует весь доступ к пользовательскому интерфейсу до тех пор, пока он не получит ответ. В качестве дополнительного бонуса this.Invoke вернет объект, который можно преобразовать в DialogResult.
Как сказали Стивен и Ханс, используйте событие ReportProgress и передавайте данные в поток UI. Это особенно важно, если вы хотите сделать что-то кроме MessageBox (например, обновить элемент управления), потому что фоновый поток не может сделать это напрямую. Вы получите межпотоковое исключение.
Поэтому, что бы вам ни нужно было сделать (обновить индикатор выполнения, записать сообщения в UI и т.д.), передайте данные потоку UI, скажите потоку UI, что нужно сделать, но пусть поток UI сделает это.