2013-08-16 126 views
5

我正在通過一個名爲Revit的建築建模軟件的API創建一個自定義加載項命令。我的命令可能需要一些時間才能完成,因此我想向用戶顯示帶有進度條的窗口。在另一個線程上創建一個WPF進度窗口

通常,如果我要創建一個像這樣的進度窗口,它將在主UI線程上,並且正在完成的實際工作將發生在輔助工作線程上。但是,Revit要求通過調用自定義命令的線程來訪問API。所以我必須在第二個線程上創建我的進度條。

我發現這篇博客文章關於launching a WPF window in a separate thread,並基於我的解決方案。這是我的自定義命令類。

public class SampleProgressWindowCommand : Autodesk.Revit.UI.IExternalCommand 
{ 

    private ProgressWindow progWindow; 
    internal static EventWaitHandle _progressWindowWaitHandle; 


    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) 
    { 
     //Starts New Progress Window Thread 
     using (_progressWindowWaitHandle = new AutoResetEvent(false)) 
     { 

      //Starts the progress window thread 
      Thread newprogWindowThread = new Thread(new ThreadStart(ShowProgWindow)); 
      newprogWindowThread.SetApartmentState(ApartmentState.STA); 
      newprogWindowThread.IsBackground = true; 
      newprogWindowThread.Start(); 

      //Wait for thread to notify that it has created the window 
      _progressWindowWaitHandle.WaitOne(); 
     } 

     //Does some work that takes a long time 
     for (int i = 1; i <= 100; i++) 
     { 
      //Updates Progress 
      this.progWindow.UpdateProgress("Item " + i.ToString(), i, 100); 

      //Does some fake work 
      System.Threading.Thread.Sleep(700); 
     } 

     //closes the Progress window 
     progWindow.Dispatcher.Invoke(new Action(progWindow.Close)); 

     //Show Result to User 
     Autodesk.Revit.UI.TaskDialog.Show("Task", "Task Completed"); 
     return Result.Succeeded; 
    } 

    private void ShowProgWindow() 
    { 
     //creates and shows the progress window 
     progWindow = new ProgressWindow(); 
     progWindow.Show(); 

     //makes sure dispatcher is shut down when the window is closed 
     progWindow.Closed +=new EventHandler(progWindow_Closed); 

     //Notifies command thread the window has been created 
     _progressWindowWaitHandle.Set(); 

     //Starts window dispatcher 
     System.Windows.Threading.Dispatcher.Run(); 
    } 
} 

這裏是我的ProgressWindow類的UpdateProgress()方法

public void UpdateProgress(string message, int current, int total) 
{   
    this.Dispatcher.Invoke(new Action<string, int, int>(

    delegate(string m, int v, int t) 
    { 
     this.progressBar1.Maximum = System.Convert.ToDouble(t); 
     this.progressBar1.Value = System.Convert.ToDouble(v); 
     this.messageLbl.Content = m; 
    }), 
    System.Windows.Threading.DispatcherPriority.Background, 
    message, current, total); 
} 

我的第一個問題是,一般沒有我這樣做對嗎?它似乎工作,但我對多線程編程足夠了解,知道僅僅因爲它今天有效,並不意味着它明天就會工作。

其次,我想添加一個取消按鈕到我的進度窗口能夠取消進程。做這個的最好方式是什麼?我明白,最終我會得到一個由工作線程定期檢查的「cancelRequested」布爾標誌,但是如何從進度窗口線程設置它?

+0

1 - 您是否在使用AutoDesk?真? 2 - 創建一個合適的ViewModel並讓ViewModel在輔助Dispatcher線程中提升屬性更改。 –

+1

Revit由Autodesk製作,但我不適用於Autodesk。通常,我所做的所有WPF都是MVVM,但爲了簡單起見,本示例沒有爲我的進度窗體創建ViewModel類。 –

+0

@EricAnastas我知道這個線程有點舊,但我沒有找到新的,我的問題非常接近這個問題。有沒有辦法更新啓動外部事件的GUI? – Thibaud

回答

3

我可以看到的唯一改進是您在設置AutoResetEvent和致電Dispatcher.Run之間有潛在的競爭條件。我知道是因爲我在自己使用多線程進度UI時遇到了這個問題。

修復方法是在BeginInvoke後臺調用Dispatcher。這將確保它在Dispatcher開始泵送事件後執行:

System.Windows.Threading.Dispatcher.Current.BeginInvoke(
    new Func<bool>(_progressWindowWaitHandle.Set)); 
相關問題