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

10/10/2013

Создание пользовательской закрепляемой панели

В Revit API 2014 появилась замечательная возможность создавать собственные закрепляемые панели, на подобие стандартных панелей Свойства и Диспетчер проекта.

Закрепляемые панели являются частью интерфейса Revit и хороши тем, что их можно зафиксировать в любой части экрана, либо, как отдельное окно, перенести, например, на другой монитор, и тем самым расширить рабочую область.

Таким образом, в Revit 2014 можно создать окно для вашей надстройки и выглядеть оно будет как часть стандартного интерфейса.

Рассмотрим как же можно добавить свою закрепляемую панель.

Закрепляемая панель является обычным окном, созданным в WPF. Пользователь может разместить в этом окне любой элемент, наследованный от класса FrameworkElement, т.е. фактически любой контрол WPF.

Для примера, создадим панель, в середине которого будет отображаться название активного документа.

Создадим WPF UserControl

Код - XML: [Выделить]
  1. <UserControl x:Class="DockablePaneSample.MyControl"
  2.              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6.              mc:Ignorable="d"
  7.              d:DesignHeight="300" d:DesignWidth="300">
  8.     <Grid Background="White">
  9.         <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding DocumentTitle}"/>
  10.     </Grid>
  11. </UserControl>

Стоит отметить, что обязательно нужно задать цвет фона. Иначе увидите черное окно.

Взаимодействие контрола с данными будем осуществлять с помощью паттерна MVVM.

Чтобы отобразить в Revit свою панель, сначала необходимо ее зарегистрировать. Для этого необходимо вызвать метод UIControlledApplication.RegisterDockablePane

Код - C#: [Выделить]
  1. public void RegisterDockablePane(
  2.         DockablePaneId id,
  3.         string title,
  4.         IDockablePaneProvider provider
  5. )

id – идентификатор панели. Просто создаем экземпляр класса DockablePaneId передав GUID в конструктор

title – заголовок панели

provider – интерфейс, содержащий метод SetupDockablePane, где необходимо задать контрол, который мы будем отображать в панели, а также параметры инициализации.

Таким образом, нам надо создать класс, реализующий интерфейс IDockablePaneProvider. Для удобства я могу порекомендовать не создавать отдельный класс, а использовать класс созданного ранее контрола, добавив реализацию интерфейса. Там же можно задать заголовок и идентификатор.

Код - C#: [Выделить]
  1.    public partial class MyControl : UserControl, IDockablePaneProvider
  2.     {
  3.         public MyControl()
  4.         {
  5.             InitializeComponent();
  6.         }
  7.  
  8.         public void SetupDockablePane(DockablePaneProviderData data)
  9.         {
  10.             data.FrameworkElement = this;
  11.         }
  12.  
  13.         public static DockablePaneId PaneId
  14.         {
  15.             get
  16.             {
  17.                 return new DockablePaneId(new Guid("E6EF9DE9-F5F2-454B-8968-4BA2622E5CE5"));
  18.             }
  19.         }
  20.  
  21.         public static string PaneName
  22.         {
  23.             get
  24.             {
  25.                 return "ADN-CIS";
  26.             }
  27.         }
  28.     }

Обратите внимание, что в методе SetupDockablePane в качестве контрола панели я передаю сам контрол.

Все готово для регистрации.

Важный момент, который я не нашел в документации. Зарегистрировать панель можно только тогда, когда нет открытого документа. А открыть ее можно только когда есть открытый документ. Т.е. в одной команде зарегистрировать и сразу же отобразить панель не получится.

Таким образом, регистрацию можно сделать двумя способами: при запуске Revit в методе OnStartUp, либо отдельной командой, которую можно запустить, когда нет открытого документа, предварительно сделав проверку, есть ли активный документ.

Вариант с регистрацией при запуске Revit мне кажется более предпочтительным. Но необходимо иметь ввиду, что не стоит перегружать класс контрола тяжелыми операциями, дабы не замедлить запуск Revit. Паттерн MVVM как раз этому способствует. Передавать данные в контрол будем позже.

Для отображения данных создадим класс MyDockablePaneViewModel, содержащий свойство DocumentTitle (в контроле мы задали свойство текст как Text="{Binding DocumentTitle}").

Код - C#: [Выделить]
  1.    public class MyDockablePaneViewModel : INotifyPropertyChanged
  2.     {
  3.         private string _documentTitle;
  4.  
  5.         public string DocumentTitle
  6.         {
  7.             get { return _documentTitle; }
  8.             set
  9.             {
  10.                 _documentTitle = value;
  11.                 OnPropertyChanged("DocumentTitle");
  12.             }
  13.         }
  14.  
  15.         public event PropertyChangedEventHandler PropertyChanged;
  16.  
  17.         [NotifyPropertyChangedInvocator]
  18.         protected virtual void OnPropertyChanged(string propertyName)
  19.         {
  20.             PropertyChangedEventHandler handler = PropertyChanged;
  21.             if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  22.         }
  23.  
  24.     }

При изменении свойства DocumentTitle   необходимо оповестить об этом контрол, чтобы произошло изменение текста. Для это используется метод OnPropertyChanged.

Так как в Revit одновременно может быть открыто несколько разных файлов, то необходимо менять название в нашей панели, в зависимости от активного файла. Для этого подпишемся на событие ViewActivated. В обработчике события будем менять свойство DocumentTitle.

Класс App выглядит следующим образом:

Код - C#: [Выделить]
  1.    class App : IExternalApplication
  2.     {
  3.         public static MyControl MyDockablePaneControl;
  4.  
  5.         public Result OnStartup(UIControlledApplication a)
  6.         {
  7.             a.ViewActivated += OnViewActivated;
  8.  
  9.             MyDockablePaneControl = new MyControl();
  10.  
  11.             MyDockablePaneViewModel dockablePaneViewModel =
  12.                new MyDockablePaneViewModel();
  13.  
  14.             MyDockablePaneControl.DataContext = dockablePaneViewModel;
  15.  
  16.             if (!DockablePane.PaneIsRegistered(MyControl.PaneId))
  17.             {
  18.                 a.RegisterDockablePane(MyControl.PaneId,
  19.                     MyControl.PaneName,
  20.                     MyDockablePaneControl);
  21.             }
  22.            
  23.  
  24.             return Result.Succeeded;
  25.         }
  26.  
  27.         private void OnViewActivated(object sender, ViewActivatedEventArgs e)
  28.         {
  29.             if (e.Document == null)
  30.                 return;
  31.  
  32.             var viewModel = MyDockablePaneControl.DataContext as MyDockablePaneViewModel;
  33.  
  34.             if (viewModel != null)
  35.             {
  36.                 viewModel.DocumentTitle = e.Document.Title;
  37.             }
  38.         }
  39.           
  40.     }

После запуска Revit, произойдет регистрация нашей панели. Чтобы ее отобразить, необходимо вызвать команду, в которой отобразим нашу панель:

Код - C#: [Выделить]
  1.    [Transaction(TransactionMode.Manual)]
  2.     public class Command : IExternalCommand
  3.     {
  4.         public Result Execute(
  5.           ExternalCommandData commandData,
  6.           ref string message,
  7.           ElementSet elements)
  8.         {
  9.             UIApplication uiapp = commandData.Application;
  10.  
  11.             if (DockablePane.PaneIsRegistered(MyControl.PaneId))
  12.             {
  13.                 DockablePane myCustomPane =
  14.                     uiapp.GetDockablePane(MyControl.PaneId);
  15.                 myCustomPane.Show();
  16.             }
  17.             else
  18.             {
  19.                 return Result.Failed;
  20.             }
  21.  
  22.             return Result.Succeeded;
  23.         }
  24.     }

Результат можно увидеть на скриншоте.

 

Все панели можно сгруппировать в одну

 

Как видим, пользовательская панель полностью встраивается в интерфейс Revit. 

Полный код с проектом для Visual Studio можно скачать здесь

Автор: Виктор Чекалин
Автор перевода: Виктор Чекалин

Обсуждение: http://adn-cis.org/forum/index.php?topic=243

Опубликовано 10.10.2013
Отредактировано 10.10.2013 в 09:19:16