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

问题描述 投票:0回答:1

我正在通过名为 Revit 的建筑建模软件的 API 创建自定义插件命令。我的命令可能需要一些时间才能完成,因此我想向用户显示一个带有进度条的窗口,因为它正在运行。

通常,如果我要创建这样的进度窗口,它将在主 UI 线程上,而实际完成的工作将在辅助工作线程上发生。但是,Revit 要求对 API 的任何访问都通过调用自定义命令的线程进行。所以我必须在第二个线程上创建进度条。

我发现这篇关于 在单独的线程中启动 WPF 窗口 的博客文章,并基于它提出了我的解决方案。这是我的自定义命令类。

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”布尔标志,但是如何从进度窗口线程中设置它?

c# .net wpf multithreading revit-api
1个回答
4
投票

我看到的唯一改进是,在设置

AutoResetEvent
和调用
Dispatcher.Run
之间存在潜在的竞争条件。我知道,因为我在使用多线程进度 UI 时遇到了这个问题。

修复的方法是

BeginInvoke
后台调用
Dispatcher
。这将确保它在
Dispatcher
开始泵送事件之后执行:

System.Windows.Threading.Dispatcher.Current.BeginInvoke(
    new Func<bool>(_progressWindowWaitHandle.Set));
© www.soinside.com 2019 - 2024. All rights reserved.