C # Как закрыть форму окна в главном потоке пользовательского интерфейса из другого потока - PullRequest
2 голосов
/ 09 декабря 2011

Я создаю приложение WPF MVVM.У меня есть длинный процесс, который я хочу запустить в другом потоке, пока отображается индикатор занятости для пользователя.У меня проблема в следующем:

Свойство IsBusy элемента управления BusyIndicator связано с открытым свойством IsBusy моей модели представления, которая реализует интерфейс INotifyPropertyChanged.Если я запускаю приведенный ниже код с помощью Join, то пользовательский интерфейс не показывает индикатор занятости, так как основной поток пользовательского интерфейса ожидает завершения потока "t".Если я удаляю объединение, то форма Windows, на которой размещается WPF, закрывается слишком рано.Я знаю, что доступ к Windows Forms через потоки - это большое «нет», но все, что я хочу сделать, это закрыть форму, я думаю, самое простое решение - переместить _hostForm.Close () в конец метода «DoLongProcess».Конечно, если я это сделаю, я получу исключение перекрестного потока.Можете ли вы предложить лучший подход в этой ситуации?

<extToolkit:BusyIndicator IsBusy="{Binding Path=IsBusy}" >
    <!-- Some controls here -->
</extToolkit:BusyIndicator>

private void DoSomethingInteresting() { 

        //  Set the IsBusy property to true which fires the 
        //  notify property changed event
        IsBusy = true;

        //  Do something that takes a long time
        Thread t = new Thread(DoLongProcess);
        t.Start();
        t.Join();

        //  We're done. Close the Windows Form
        IsBusy = false;
        _hostForm.Close();

    }

Ответы [ 3 ]

8 голосов
/ 09 декабря 2011

Лучшее, что нужно сделать в этой ситуации, - прежде чем вы на самом деле вызовете закрытие формы, вы уведомите все свои системы, которые собираетесь закрыть, что даст вам возможность запустить любой процесс в конце. Когда вы закончите и захотите закрыть форму из другого потока, вам нужно будет вызвать ее в потоке пользовательского интерфейса, используя:

_hostForm.BeginInvoke(new Action(() => _hostForm.Close()));

Возможно, было бы лучше, если бы вы всегда закрывали форму из другого потока, чтобы на самом деле создать потокобезопасную версию метода close; i.e.:

public class MyForm : Form
{
    // ...

    public void SafeClose()
    {
        // Make sure we're running on the UI thread
        if (this.InvokeRequired)
        {
            BeginInvoke(new Action(SafeClose));
            return;
        }

        // Close the form now that we're running on the UI thread
        Close();
    }

    // ...
}

Используя такой подход, вы можете продолжать обновлять форму и ее интерфейс во время выполнения асинхронных операций, а затем вызывать завершение работы и очистку по завершении.

3 голосов
/ 09 декабря 2011

Я предлагаю вам использовать BackgroundWorker класс. Следуйте этому примеру:

BackgroundWorker wrk = new BackgroundWorker();
            wrk.WorkerReportsProgress = true;
            wrk.DoWork += (a, b) =>
            {
                ... your complex stuff here
            };

            wrk.RunWorkerCompleted += (s, e) =>
                {
                   isBusy=false;
                   _hostForm.Close();
                };
            wrk.RunWorkerAsync();

Код внутри полного RunWorker уже находится в потоке пользовательского интерфейса. Это решение не является блокирующим, поэтому вы можете видеть, что isBusy изменен, а пользовательский интерфейс реагирует правильно. Часть DoWork выполняется в другом потоке, но вы также можете использовать функцию ReportProgress, если хотите.

2 голосов
/ 09 декабря 2011

Вот мое предложение для вас.Я решил бы это с Tasks из TPL, содержащегося в .NET Framework начиная с версии 4:

private void DoSomethingInteresting() 
{
    IsBusy = true;

    var task = new Task(() => DoLongProcess());
    task.ContinueWith(previousTask =>
                      {
                          IsBusy = false;
                          _hostForm.Close();
                      }, TaskScheduler.FromCurrentSynchronizationContext());

    task.Start();
} 

Редактировать

Объяснение: Работа выполняется в одной задачев фоновом режиме.Когда эта задача выполнена, вторая задача .ContinueWith... запускается автоматически и запускается в потоке пользовательского интерфейса из-за TaskScheduler.FromCurrentSynchronizationContext().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...