Получение сообщения в главном приложении с помощью Backgroundworker в WPF - PullRequest
9 голосов
/ 23 июня 2010

В приложении WPF я использую BackgroundWorker для периодической проверки состояния на сервере. Хотя это работает нормально, я хочу открыть MessageBox, уведомляя пользователей, если что-то не получается во время проверки.

Вот что у меня есть:

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.

Как мне это решить? Спасибо,


EDIT:

Для справки, вот что я в итоге сделал:

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");
}

Ответы [ 5 ]

10 голосов
/ 23 июня 2010

Оно не только не блокирует главное окно, но также может исчезнуть за ним. Это прямое следствие того, что он работает в другом потоке. Когда вы не указываете владельца для окна сообщения, оно ищет его с помощью функции API GetActiveWindow (). Он рассматривает только те окна, которые используют одну и ту же очередь сообщений. Что является специфичным для потока свойством. Потерять окно сообщения довольно сложно, конечно.

Аналогично, MessageBox отключает только окна, принадлежащие одной и той же очереди сообщений. И, таким образом, не будет блокировать окна, созданные вашим основным потоком.

Исправьте свою проблему, позволив потоку пользовательского интерфейса отобразить окно сообщения. Используйте Dispatcher.Invoke или используйте события ReportProgress или RunWorkerCompleted. Не похоже, что события здесь уместны.

6 голосов
/ 23 июня 2010

Позвоните ReportProgress и передайте this на MessageBox.Show.

3 голосов
/ 23 июня 2010

Замените

MessageBox.Show("I should block the main window"); 

на

this.Invoke((Func<DialogResult>)(() => MessageBox.Show("I should block the main window")));

, что приведет к тому, что окно сообщения будет находиться в главном потоке, и заблокирует весь доступ к пользовательскому интерфейсу до получения ответа.В качестве дополнительного бонуса this.Invoke вернет объект, который может быть приведен к DialogResult.

2 голосов
/ 23 июня 2010

Как сказали Стивен и Ханс, используйте событие ReportProgress и передавайте данные в поток пользовательского интерфейса. Это особенно важно, если вы хотите сделать что-то другое, чем MessageBox (для isntance, обновить элемент управления), потому что фоновый поток не может сделать это напрямую. Вы получите исключение между потоками.

Так что все, что вам нужно сделать (обновить индикатор выполнения, записать сообщения в пользовательский интерфейс и т. Д.), Передать данные в поток пользовательского интерфейса, сообщить потоку пользовательского интерфейса, что необходимо сделать, но пусть поток пользовательского интерфейса сделает это.

1 голос
/ 29 сентября 2015

Я изменил это так и работал нормально для меня

 return Application.Current.Dispatcher.Invoke(() => MessageBox.Show(messageBoxText, caption, button, icon));
...