7

Revit という建築モデリング ソフトウェアの API を使用して、カスタム アドイン コマンドを作成しています。コマンドが完了するまでに時間がかかる場合があるため、進行状況バーを含むウィンドウをユーザーに表示したいと考えています。

通常、このような進行状況ウィンドウを作成する場合、それはメイン UI スレッド上にあり、実行中の実際の作業はセカンダリ ワーカー スレッド上で発生します。ただし、Revit では、カスタム コマンドを呼び出すスレッドを介して API にアクセスする必要があります。したがって、2 番目のスレッドでプログレス バーを作成する必要があります。

別のスレッドで 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」ブール値フラグになることを理解していますが、進行状況ウィンドウのスレッドからこれを設定するにはどうすればよいですか?

4

1 に答える 1

4

私が確認できる唯一の改善点は、設定AutoResetEventと呼び出しの間に競合状態が発生する可能性があることですDispatcher.Run。マルチスレッド プログレス UI を自分で使用しているときにこの問題に遭遇したので、私は知っています。

それを修正する方法はBeginInvoke、バックグラウンドで呼び出しを行うことDispatcherです。Dispatcherこれにより、イベントのポンピングが開始された後に確実に実行されます。

System.Windows.Threading.Dispatcher.Current.BeginInvoke(
    new Func<bool>(_progressWindowWaitHandle.Set));
于 2013-08-16T19:08:16.310 に答える