简体   繁体   English

在另一个线程上创建WPF进度窗口

[英]Create a WPF Progress Window on another thread

I'm creating a custom add-in command through the API of a piece of architectural modeling software called Revit. 我正在通过一个名为Revit的架构建模软件的API创建一个自定义加载项命令。 My command may take some time to complete so I want to show the user a window with a progress bar as it is working. 我的命令可能需要一些时间才能完成,所以我想向用户显示一个带有进度条的窗口,因为它正在工作。

Typically if I were to create a progress window like this it would be on the main UI thread, and the actual work being done would happen on a secondary worker thread. 通常,如果我要创建这样的进度窗口,它将位于主UI线程上,并且正在完成的实际工作将在辅助工作线程上进行。 However, Revit requires that any access to the API be through the thread calling the custom command. 但是,Revit要求对API的任何访问都是通过调用自定义命令的线程进行的。 So I must create my progress bar on a second thread. 所以我必须在第二个线程上创建我的进度条。

I found this blog post about launching a WPF window in a separate thread , and based my solution on it. 我发现这篇关于在单独的线程中启动WPF窗口的博客文章,并基于我的解决方案。 Here's my custom command class. 这是我的自定义命令类。

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

And here is the UpdateProgress() method on my ProgressWindow class 这是我的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);
}

My first question is in general did I do this right? 我的第一个问题一般是我做对了吗? It seems to work, but I know enough about multithreaded programing to know that just because it works today, does not mean it's going to work tomorrow. 它似乎有用,但我对多线程编程知之甚少,因为它知道它只是因为它今天起作用,并不意味着它明天会起作用。

Second, I would like to add a cancel button to my progress window to be able to cancel the process. 其次,我想在我的进度窗口中添加一个取消按钮,以便能够取消该过程。 What is the best way to do this? 做这个的最好方式是什么? I understand that ultimately I'll end up with a "cancelRequested" boolean flag that is checked regularly by the working thread, but how do I set this from the progress window thread? 我明白最终我会得到一个“cancelRequested”布尔标志,该标志由工作线程定期检查,但是如何从进度窗口线程设置它?

The only improvement that I can see is that you have a potential race condition between setting your AutoResetEvent and calling Dispatcher.Run . 我能看到的唯一改进是,在设置AutoResetEvent和调用Dispatcher.Run之间存在潜在的竞争条件。 I know because I've run into this issue in my own use of multi-threaded progress UIs. 我知道因为我在自己使用多线程进度UI时遇到了这个问题。

The way to fix it is to BeginInvoke the call on the background Dispatcher . 修复它的方法是在后台DispatcherBeginInvoke调用。 This will ensure it executes after the Dispatcher has begun pumping events: 这将确保在Dispatcher开始抽取事件后执行:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM