2011-12-09 14 views
2

私はWPF MVVMアプリケーションを作成しています。ビジー状態のインジケーターをユーザーに表示しながら、別のスレッドで実行したいという長いプロセスがあります。私が持っている問題は、次のとおりです。C#別のスレッドからのメインUIスレッドのウィンドウフォームを閉じるには

BusyIndi​​catorコントロールのIsBusyプロパティは、INotifyPropertyChangedインターフェイスを実装するビューモデルのIsBusyパブリックプロパティにバインドされています。下のコードをJoinで実行すると、メインUIスレッドがスレッド「t」が終了するのを待っているため、ユーザーインターフェイスにビジーインジケータが表示されません。参加を取り除くと、WPFをホストしているWindowsフォームが早すぎて閉じます。スレッド間でWindowsフォームにアクセスすることは大きな問題ではありませんが、私がしたいのはフォームを閉じることです。最も簡単な解決策は_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

http://stackoverflow.com/a/947482/532647 –

答えて

8

このような状況で行うための最善のことは、あなたが実際にフォームの閉鎖を起動する前に、あなたはあなたの任意のプロセスを実行する機会を与えるあなたが閉鎖しようとしているすべてのシステムを、通知しています終わり。あなたが完成し、他のスレッドからフォームを閉じたいときは、使用してUIスレッド上で、それを呼び出す必要があります:

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

あなたはいつものフォームを閉じる可能性がある場合は、良いかもしれませんcloseメソッドのスレッドセーフバージョンを実際に作成する別のスレッドすなわち:この種のアプローチを使用して

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

    // ... 
} 

、フォームを更新し続けることができ、それが非同期操作を実行している間、あなた終了したら、その後、シャットダウンおよびクリーンアップを起動するUIです。

+0

あなたのお返事ありがとうございます。私はこの答えを選択しました。これは、.NET 3.5でうまく動作していて、使用する必要があることを指定していないためです。他の人は、彼らの答えにも功績を認めています。再度、感謝します。 – dior001

3

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 complete内のコードは、すでにUIスレッドにあります。このソリューションは非ブロッキングであるため、isBusyが変更され、UIが適切に反応することがわかります。 DoWork部分は別のスレッドで実行されますが、必要に応じてReportProgress機能を使用することもできます。

2

ここに私の提案があります。私は、バージョン4以降の.NET Frameworkに含まれているTPLからTasksでそれを解決するだろう:

private void DoSomethingInteresting() 
{ 
    IsBusy = true; 

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

    task.Start(); 
} 

編集し

説明:作業はバックグラウンドで一つのタスクで実行されます。このタスクが完了すると、.ContinueWith...という2番目のタスクが自動的に開始され、TaskScheduler.FromCurrentSynchronizationContext()のためにUIスレッドで実行されます。

関連する問題