8. Revit API UI: DockablePane(可停靠窗口)
接着前面的预览控件和外部事件,这一篇,我们来看看可停靠窗口DockablePane
的实现。
实现流程
可停靠窗口相关的类与接口主要有DockablePane
和IDockablePageProvider
,都是在UI
命名空间下。
流程:
- 创建一个
Page
页,并实现IDockablePageProvider
接口; - 生成一个
DockablePaneId
,需要传入一个Guid
,所以最好建立一个映射表; - 在插件入口(即
IExteranlApplication
)中注册该窗口,使用UIControlledApplication.RegisterDockablePane(..)
方法; - 使用时,通过
uiApp.GetDockablePane(dpId)
方法检索到可停靠窗体,调用Show()
显示窗体。
先实现,后面再讲讲我个人测试的遇到的问题。
示例
本例想接着用上一篇用到的预览窗体界面,但Revit启动时显然是没有View
的,就算了。
-
XAML
:新建一个叫"DockablePage"的Page
,里头随便放些东西。记得要实现IDockablePageProvider
接口。 -
IExternalApplication
:在外部应用启动时注册。
[Transaction(TransactionMode.Manual)]
[Journaling(JournalingMode.NoCommandData)]
public class Application : IExternalApplication
{public Result OnStartup(UIControlledApplication application){this.RegisterDockablePane(application);return Result.Succeeded;}public Result OnShutdown(UIControlledApplication application){return Result.Succeeded;}private void RegisterDockablePane(UIControlledApplication application){DockablePaneId dockablePaneId = new DockablePaneId(new Guid(Mapping.DockablePaneId)); // 生成IDapplication.RegisterDockablePane(dockablePaneId, "DockablePane", DockablePage.GetDockablePage()); // 进行注册}
}
IExternalCommand
:在外部命令中使用。
[Transaction(TransactionMode.Manual)]
[Journaling(JournalingMode.NoCommandData)]
internal class DockablePaneCommand : IExternalCommand
{public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements){UIApplication uiApp = commandData.Application;DockablePaneId dockablePaneId = new DockablePaneId(new Guid(Mapping.DockablePaneId)); // 创建ID,根据ID查找//TaskDialog.Show("DockablePaneCommand", $"{DockablePane.PaneIsRegistered(dockablePaneId)}\r\n" + $"{DockablePane.PaneExists(dockablePaneId)}");DockablePane dockablePane = uiApp.GetDockablePane(dockablePaneId);dockablePane.Show();return Result.Succeeded;}
}
至此,一个完整的可停靠窗口的Demo就完成了。
记录问题
1. 必须在外部应用中注册吗
如果你了解Application
相关API,就能够知道UIControlledApplication
和UIApplication
的大部分类成员都是一样的,效果一样。不了解没关系可以看这篇:5. Revit API: Application。
那么,就一定得在外部应用中注册?
是的,必须在外部应用中注册。
能不能在外部命令中,通过UIApplication注册呢?
答案是可以的,但是只能注册,无法显示。
DockablePane
类中有3个静态方法,通过传入DockablePaneId
进行检测。
方法 | 描述 |
---|---|
PaneIsBuiltIn(dpID) | 检测目标窗口是否为Revit内置窗口 |
PaneIsRegistered(dpID) | 检测目标窗口是否已注册 |
PaneExists(dpID) | 检测目标窗口是否已创建 |
通过这三个方法,可以得到结果:
注册位置 | 内置 | 已注册 | 已创建 |
---|---|---|---|
IExternalApplication | ❌ | ✔ | ✔ |
IExternalCommand | ❌ | ✔ | ❌ |
当在外部命令中注册,并调用Show()
方法时,会报错。
2. 必须用Page吗
虽然API文档中写建议使用Page及其派生类。那我可偏要看看不用能咋样。
好吧,不用界面就黑了。
Window
不行,Page
和UserControl
都可以。Window是最高级别的UI元素,猜测非Window都可以。
下面是使用Window和UserControl的截图
3. 可停靠窗口中,可以放外部事件吗
可以
上面说了,最开始是想直接使用上一篇的界面,那上面就有ExternalEvent
,我测试了那个删除按钮还是有效的。
4. 如何让可停靠窗口的内容变化
例子中创建了个表格,就是想从项目文档中获取信息,进行展示。就像Revit自带的那些窗口一样,内容可以变化,想想就很棒是不是,可惜不得行。
不知道,不会,没搞定😶
好吧,没搞定。
DockablePane
类中,并没有获取窗体的方法或属性,于是尝试曲线修改。
-
尝试让数据表绑定在一个静态变量上,可是数据变了没效果。
-
尝试将页面设为单例,改了还是无效。
然后就没有然后了,怀疑注册时传进去的和Revit创建的就不是同一个东西。
5. 遇到的其它问题
遇到了一个"无法初始化附加模块xxx,因为“…/xx.dll”不存在"的问题。
显然这破问题的原因不在于那个dll不存在。
问题在哪儿呢?还是之前写Ribbon
时偷懒了,直接用"xxx"代替了。
这里问题的原因是,Ribbon控件里,命令的路径错了
Ribbon那篇没有示例图片,这里补充一个吧。
private void CreateRibbon(UIControlledApplication application)
{string tabName = "Samples";string panelName = "Commands";RibbonPanel panel = application.CreatePanel(panelName, tabName);PushButtonData buttonData_single = new PushButtonData("单个按钮", "单个按钮", addInPath, "RevitAPISamples.Commands.Dialog"){LargeImage = new BitmapImage(new Uri(Path.Combine(this.buttonIconsFolder, "CreateWall.png"))),Image = new BitmapImage(new Uri(Path.Combine(this.buttonIconsFolder, "CreateWall.png"))),ToolTip = "按钮(单个)",LongDescription = "鼠标放按钮上,就会出现这段文字",ToolTipImage = new BitmapImage(new Uri(Path.Combine(this.buttonIconsFolder, "Revit.png"))),};ContextualHelp contextualHelp = new ContextualHelp(ContextualHelpType.Url, "https://thebuildingcoder.typepad.com/blog/2020/09/on-spaces-in-help-and-renaming-a-parameter.html");buttonData_single.SetContextualHelp(contextualHelp);panel.AddItem(buttonData_single);
}
总结
UI
篇还有多少来着,不记得了,得去翻翻之前的图。
这篇就到这里了,下篇写什么呢,emm…。