2013-08-16 127 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)); 
相关问题