2013-08-16 3 views
5

Я создаю пользовательскую команду надстройки через API части программного обеспечения для архитектурного моделирования под названием Revit. Моя команда может занять некоторое время, поэтому я хочу показать пользователю окно с индикатором выполнения, когда оно работает.Создайте окно прогресса WPF в другом потоке

Как правило, если бы я должен был создать окно прогресса, подобное этому было бы на основном потоке пользовательского интерфейса, и фактическая работа выполнялась бы на вторичном рабочем потоке. Однако 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(); 
    } 
} 

А вот метод UpdateProgress() в моем классе ProgressWindow

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 во вторичном потоке Диспетчера. –

+1

Revit создан Autodesk, но я не работаю для Autodesk. Как правило, все WPF, которые я делаю, это MVVM, но для простоты в этом примере не был создан класс ViewModel для моей формы выполнения. –

+0

@ EricAnastas Я знаю, что эта тема немного устарела, но я не нашел ее более новой, и мой вопрос очень близок к этой теме. Есть ли способ обновить GUI, который запустил внешнее событие? – Thibaud

ответ

3

Единственное улучшение, которое я вижу, заключается в том, что у вас есть потенциальное состояние гонки между настройкой AutoResetEvent и вызовом Dispatcher.Run. Я знаю, потому что я столкнулся с этой проблемой в своем собственном использовании многопоточных пользовательских интерфейсов прогресса.

Способ исправить это BeginInvoke звонок по фону Dispatcher. Это обеспечит его выполнение после того, как Dispatcher начал накачки:

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

 Смежные вопросы

  • Нет связанных вопросов^_^