Создание пользовательской закрепляемой панели
В Revit API 2014 появилась замечательная возможность создавать собственные закрепляемые панели, на подобие стандартных панелей Свойства и Диспетчер проекта.
Закрепляемые панели являются частью интерфейса Revit и хороши тем, что их можно зафиксировать в любой части экрана, либо, как отдельное окно, перенести, например, на другой монитор, и тем самым расширить рабочую область.
Таким образом, в Revit 2014 можно создать окно для вашей надстройки и выглядеть оно будет как часть стандартного интерфейса.
Рассмотрим как же можно добавить свою закрепляемую панель.
Закрепляемая панель является обычным окном, созданным в WPF. Пользователь может разместить в этом окне любой элемент, наследованный от класса FrameworkElement, т.е. фактически любой контрол WPF.
Для примера, создадим панель, в середине которого будет отображаться название активного документа.
Создадим WPF UserControl
- <UserControl x:Class="DockablePaneSample.MyControl"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- mc:Ignorable="d"
- d:DesignHeight="300" d:DesignWidth="300">
- <Grid Background="White">
- <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding DocumentTitle}"/>
- </Grid>
- </UserControl>
Стоит отметить, что обязательно нужно задать цвет фона. Иначе увидите черное окно.
Взаимодействие контрола с данными будем осуществлять с помощью паттерна MVVM.
Чтобы отобразить в Revit свою панель, сначала необходимо ее зарегистрировать. Для этого необходимо вызвать метод UIControlledApplication.RegisterDockablePane
- public void RegisterDockablePane(
- DockablePaneId id,
- string title,
- IDockablePaneProvider provider
- )
id – идентификатор панели. Просто создаем экземпляр класса DockablePaneId передав GUID в конструктор
title – заголовок панели
provider – интерфейс, содержащий метод SetupDockablePane, где необходимо задать контрол, который мы будем отображать в панели, а также параметры инициализации.
Таким образом, нам надо создать класс, реализующий интерфейс IDockablePaneProvider. Для удобства я могу порекомендовать не создавать отдельный класс, а использовать класс созданного ранее контрола, добавив реализацию интерфейса. Там же можно задать заголовок и идентификатор.
- public partial class MyControl : UserControl, IDockablePaneProvider
- {
- public MyControl()
- {
- InitializeComponent();
- }
- public void SetupDockablePane(DockablePaneProviderData data)
- {
- data.FrameworkElement = this;
- }
- public static DockablePaneId PaneId
- {
- get
- {
- return new DockablePaneId(new Guid("E6EF9DE9-F5F2-454B-8968-4BA2622E5CE5"));
- }
- }
- public static string PaneName
- {
- get
- {
- return "ADN-CIS";
- }
- }
- }
Обратите внимание, что в методе SetupDockablePane в качестве контрола панели я передаю сам контрол.
Все готово для регистрации.
Важный момент, который я не нашел в документации. Зарегистрировать панель можно только тогда, когда нет открытого документа. А открыть ее можно только когда есть открытый документ. Т.е. в одной команде зарегистрировать и сразу же отобразить панель не получится.
Таким образом, регистрацию можно сделать двумя способами: при запуске Revit в методе OnStartUp, либо отдельной командой, которую можно запустить, когда нет открытого документа, предварительно сделав проверку, есть ли активный документ.
Вариант с регистрацией при запуске Revit мне кажется более предпочтительным. Но необходимо иметь ввиду, что не стоит перегружать класс контрола тяжелыми операциями, дабы не замедлить запуск Revit. Паттерн MVVM как раз этому способствует. Передавать данные в контрол будем позже.
Для отображения данных создадим класс MyDockablePaneViewModel, содержащий свойство DocumentTitle (в контроле мы задали свойство текст как Text="{Binding DocumentTitle}").
- public class MyDockablePaneViewModel : INotifyPropertyChanged
- {
- private string _documentTitle;
- public string DocumentTitle
- {
- get { return _documentTitle; }
- set
- {
- _documentTitle = value;
- OnPropertyChanged("DocumentTitle");
- }
- }
- public event PropertyChangedEventHandler PropertyChanged;
- [NotifyPropertyChangedInvocator]
- protected virtual void OnPropertyChanged(string propertyName)
- {
- PropertyChangedEventHandler handler = PropertyChanged;
- if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
- }
- }
При изменении свойства DocumentTitle необходимо оповестить об этом контрол, чтобы произошло изменение текста. Для это используется метод OnPropertyChanged.
Так как в Revit одновременно может быть открыто несколько разных файлов, то необходимо менять название в нашей панели, в зависимости от активного файла. Для этого подпишемся на событие ViewActivated. В обработчике события будем менять свойство DocumentTitle.
Класс App выглядит следующим образом:
- class App : IExternalApplication
- {
- public static MyControl MyDockablePaneControl;
- public Result OnStartup(UIControlledApplication a)
- {
- a.ViewActivated += OnViewActivated;
- MyDockablePaneControl = new MyControl();
- MyDockablePaneViewModel dockablePaneViewModel =
- new MyDockablePaneViewModel();
- MyDockablePaneControl.DataContext = dockablePaneViewModel;
- if (!DockablePane.PaneIsRegistered(MyControl.PaneId))
- {
- a.RegisterDockablePane(MyControl.PaneId,
- MyControl.PaneName,
- MyDockablePaneControl);
- }
- return Result.Succeeded;
- }
- private void OnViewActivated(object sender, ViewActivatedEventArgs e)
- {
- if (e.Document == null)
- return;
- var viewModel = MyDockablePaneControl.DataContext as MyDockablePaneViewModel;
- if (viewModel != null)
- {
- viewModel.DocumentTitle = e.Document.Title;
- }
- }
- }
После запуска Revit, произойдет регистрация нашей панели. Чтобы ее отобразить, необходимо вызвать команду, в которой отобразим нашу панель:
- [Transaction(TransactionMode.Manual)]
- public class Command : IExternalCommand
- {
- public Result Execute(
- ExternalCommandData commandData,
- ref string message,
- ElementSet elements)
- {
- UIApplication uiapp = commandData.Application;
- if (DockablePane.PaneIsRegistered(MyControl.PaneId))
- {
- DockablePane myCustomPane =
- uiapp.GetDockablePane(MyControl.PaneId);
- myCustomPane.Show();
- }
- else
- {
- return Result.Failed;
- }
- return Result.Succeeded;
- }
- }
Результат можно увидеть на скриншоте.
Все панели можно сгруппировать в одну
Как видим, пользовательская панель полностью встраивается в интерфейс Revit.
Полный код с проектом для Visual Studio можно скачать здесь.
Автор перевода: Виктор Чекалин
Обсуждение: http://adn-cis.org/forum/index.php?topic=243
Опубликовано 10.10.2013Отредактировано 10.10.2013 в 09:19:16