Сообщество программистов Autodesk в СНГ

ADN Club => Revit API => Тема начата: Александр Пекшев aka Modis от 25-06-2019, 18:34:02

Название: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 25-06-2019, 18:34:02
Всем привет. Не знаю как точно назвать тему. Сейчас объясню суть проблемы:
Имеется WPF Page, отображаемая в Dockable Panel. Там всего одна кнопка, которая запускает какую-то работу, используя IExternalEventHandler.
Проблема в том, что во время выполнения какой-либо команды (например, простановка размеров) можно перевести мышку на панель и нажать кнопку. Если по кнопке выполняется только IExternalEventHandler, то проблем особо нет - задача ставится в очередь и выполняется после завершения текущей команды. Но вот проблемы возникают, когда до выполнения IExternalEventHandler происходят еще какие-то действия. В моем случае происходит обращение к веб-приложению, потом стартует окно в отдельном потоке (Thread) и только потом запускается IExternalEventHandler.
С точки зрения юзабилити какая-то хрень получается.

Вопрос - как можно поступить в этом случае?

P.S. Вопрос без кода, но думаю имитировать его не сложно. Можно даже взять стандартный пример (https://thebuildingcoder.typepad.com/blog/2013/05/a-simpler-dockable-panel-sample.html) и по кнопке в панели открыть окно WPF
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Игнатович от 25-06-2019, 22:30:21
С точки зрения юзабилити какая-то хрень получается.

Только сейчас, на самом деле, заметил xD

У Тэммика есть статья 2014 года на эту тем - https://thebuildingcoder.typepad.com/blog/2014/03/determining-the-quiescent-state.html

Из неё цитата:
Цитировать
In typical scenarios, end users interact with an external modeless dialogue only when they do not do anything else In Revit at that time. That is why using External events works very well for that kind of applications. The dialogue's controls are enabled all the time except for the time between raising an external event and handling it.

Вообще, хорошего способа, походу и нет (в API так точно с тех пор на эту тему ничего не появилось). Разве что в Idling перезаписывать время вызова, из отдельного thread-а смотреть, сколько времени прошло, но тут будет деградация по производительности, альтернатива можно winAPI посмотреть или, может, читать текущий журнал
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 25-06-2019, 22:36:26
Только сейчас, на самом деле, заметил xD
Сам только сегодня узнал об этом. Причем мне сообщили словами "Да это давно уже такое. Мы просто не говорили" =)
Вообще, хорошего способа, походу и нет
Да вот тоже ничего в голову не приходит. Буду думать - может придумаю способ, который не будет затратным
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Игнатович от 25-06-2019, 23:09:43
Можно попробовать, например, завести отдельный ExternalEvent с пустым методом Execute, который дергать в разумные интервалы, скажем 0,3-0,5 секунд и проверять результат который возвращает метод ExternalEvent.Raise, если вернулось не Accepted, значит включаем "занят", когда в следующий раз стало Accepted - значит окей.
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Andant от 26-06-2019, 09:18:47
Булевский статический флажок не поможет?
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 26-06-2019, 09:21:04
Булевский статический флажок не поможет?
Поможет, если придумаете как его переключать :)
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Andant от 26-06-2019, 09:43:03
Код - C# [Выбрать]
  1.  public class MyHandler : IExternalEventHandler
  2.     {
  3.         public static bool Process = false;
  4.  
  5.         public string GetName()
  6.         {
  7.             return "MyHandler";
  8.         }
  9.  
  10.         public void Execute(UIApplication uiapp)
  11.         {
  12.             if (Process)
  13.                 return;
  14.             Process = true;
  15.             try
  16.             {
  17.                 ....
  18.             }
  19.             catch
  20.             {
  21.             }
  22.             finally
  23.             {
  24.                 Process = false;
  25.             }
  26.         }
  27.     }
  28.  
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 26-06-2019, 09:47:27
Andant, это вы сможете так помечать свои собственные команды, а речь идет вообще о глобальных командах: ваших, других плагинов, Ревита - да неважно.
В автокаде с этим проще - там есть события CommandStarted, CommandEnded (и еще парочка). А вот в Ревите ничего подобного в АПИ не имеется, к сожалению
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Ривилис от 26-06-2019, 09:47:44
Andant,
Вопрос не в том, чтобы не запускать повторно свою команду, если ОНА уже запущена, а не запускать свою команду, если запущена ЛЮБАЯ команда.
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Andant от 26-06-2019, 09:53:40
Не разобрался в сути вопроса... :-[
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 26-06-2019, 09:56:19
Не разобрался в сути вопроса... :-[
Ничего страшного)) Я тоже постоянно не по теме отвечаю  :D :D :D Александр Ривилис может подтвердить =)
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Игнатович от 26-06-2019, 12:30:06
Вот Вам решение, вроде бы всё отлично работает, позже на github выложу полное.

Индикатор занятости приложения:

Код - C# [Выбрать]
  1. public class ApplicationStateIndicator : IExternalEventHandler, IDisposable
  2. {
  3.         private Timer timer;
  4.         private ExternalEvent externalEvent;
  5.        
  6.         public bool IsBusy { get; private set; }
  7.  
  8.         public event EventHandler StateChanged;
  9.  
  10.         public void Initialize(TimeSpan interval)
  11.         {
  12.                 externalEvent = ExternalEvent.Create(this);
  13.  
  14.                 externalEvent.Raise();
  15.  
  16.                 timer = new Timer(Callback, null, TimeSpan.Zero, interval);
  17.         }
  18.  
  19.         public void Execute(UIApplication app)
  20.         {
  21.                
  22.         }
  23.  
  24.         public string GetName() => nameof(ApplicationStateIndicator);
  25.  
  26.         private void Callback(object state)
  27.         {
  28.                 var pendingState = externalEvent.IsPending;
  29.  
  30.                 if (pendingState != IsBusy)
  31.                         StateChanged?.Invoke(this, EventArgs.Empty);
  32.  
  33.                 IsBusy = externalEvent.IsPending;
  34.  
  35.                 externalEvent.Raise();
  36.         }
  37.  
  38.         public void Dispose() => timer.Dispose();
  39. }

Ну и моделька mvvm:
Код - C# [Выбрать]
  1. public class Model : ViewModelBase
  2. {
  3.         private static readonly Lazy<Model> InstanceObj = new Lazy<Model>(() => new Model());
  4.  
  5.         private Model()
  6.         {
  7.                 var stateIndicator = new ApplicationStateIndicator();
  8.  
  9.                 stateIndicator.StateChanged += (sender, args) =>
  10.                         {
  11.                                 IsAvailable = stateIndicator.IsBusy;
  12.  
  13.                                 RaisePropertyChanged(() => IsAvailable);
  14.                         };
  15.  
  16.                 stateIndicator.Initialize(TimeSpan.FromSeconds(0.5));
  17.         }
  18.  
  19.         public static Model Instance => InstanceObj.Value;
  20.  
  21.         public bool IsAvailable { get; private set; }
  22. }

Тут есть какая-то несостыковка, по-идее, должно быть IsAvailable = !stateIndicator.IsBusy; Но код рабочий. Разберусь попозже, как время будет
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 27-06-2019, 11:26:54
Александр Игнатович, супер. Добавил в свой проект, тестирую - все работает как надо
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 27-06-2019, 12:36:51
Забиндил результат проверки к свойству IsEnabled кнопки в панели... Сейчас наблюдаю интересную картину - кнопка мигает постоянно. Получается, что фоном выполняется какая-то работа с примерно таким-же промежутком времени?
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Игнатович от 27-06-2019, 12:50:38
Я, честно говоря, накидал по-быстрому решение, у меня, вроде бы, всё норм, также забиндил на IsEnabled кнопки, вроде морганий не наблюдал.

Вообще, думаю, нужно поправить здесь:
Код - C# [Выбрать]
  1.                 IsBusy = externalEvent.IsPending;
  2.  
  3.                 externalEvent.Raise();

Вызывать Raise, если только IsPending false.
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 27-06-2019, 13:47:55
Вызывать Raise, если только IsPending false.
Нет, не помогает
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Игнатович от 27-06-2019, 14:40:21
А интервал не меняли? Может, слишком маленький поставили и Revit не успевает обрабатывать и ставит IsPending?
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Пекшев aka Modis от 27-06-2019, 14:42:54
А интервал не меняли? Может, слишком маленький поставили и Revit не успевает обрабатывать и ставит IsPending?
Я сначала оставил 0.5, затем ставил 2 секунды. Эффект был тот-же, но на 2-х секундах вдобавок появляется ощущение тормознутости. Т.е. 0,5 самое то - нужно только понять в чем проблема мигания
З.Ы. Пока отключил совсем
Название: Re: Dockable panel: можно ли блокировать содержимое, если работает команда?
Отправлено: Александр Игнатович от 27-06-2019, 17:09:07
Я сегодня вечером уезжаю, вернусь во вторник, там поковыряюсь с ним, самому интересно, правда, мне, сначала, нужно добиться моргания) Обычный UserControl с кнопкой у меня работает как часы, безо всяких моргалок...