2011-12-09 65 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

這是我的建議。我會解決與Tasks從TPL包含在.NET框架,因爲第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()而在UI線程上運行。