Building Coder 链接:http://thebuildingcoder.typepad.com/blog/2010/06/place-family-instance.html
Revit 二次开发论坛链接:http://revit.5d6d.com/viewthread.php?tid=1290&extra=
目前的 Revit SDK(2010/06/20)没有提供任何调用 PromptForFamilyInstancePlacement() 方法的例子。这个方法允许用户交互地可视化放置一个或多个族实例。
所以我实现了一个新的外部命令来介绍它的用法。同时我也想通过回答下面的问题来讨论一些相关的细节。
问题:
我打算使用 PromptForFamilyInstancePlacement() 方法。但是因为它没有任何返回值(void),所以我在获取刚刚创建的族实例时遇到了麻烦。我想知道有没有
什么合适的方式可以让我取得那些新建的族实例。然后我就可以对它们进行诸如设置特定参数之类的操作了。
回答:
我已经在博文《获取新增的元素》中讨论过类似的问题了。那篇博文的思路是通过递增的 element id 来得到新增见的族实例。不过在对那篇博文
的回复中,Guy Robinson 提到了一个更可靠和规范的方法:注册 DocumentChanged 事件,并在事件响应函数里记录包含在事件参数中的新增族实例的 element id。
在我们深入 Guy 的解决方案之前,让我们先来看看 PromptForFamilyInstancePlacement() 方法本身。
Revit API 帮助文件中是这么描述这个方法的:
该方法会创建其自身的事务,所以不能在一个活动的事务中调用它(译者注:会造成事物嵌套异常)。在调用该方法之后,用户可以放置任意多个指定类型的族实例,
直到用户确认完成了放置(取消/按ESC键/在用户界面的其它位置单击鼠标)。在放置过程中,用户不能改变族类型,也不能切换当前视图(切换视图的操作会导致该
方法调用结束)。
你可能想知道在一个自动提交(automatic)的外部命令中调用该方法会有什么结果(Revit 会为自动提交的外部命令自动创建一个事务)。结果是 Revit 会抛出一个
异常:Autodesk.Revit.Exceptions.InvalidOperationException: "It's not permitted to run this API method in an active transaction."
换句话说,这个方法只能在手动提交(manual)的外部命令中调用。该方法只有一个输入参数:希望被创建实例的族类型。
获取新增的元素:
如我在开始时提到的,如果希望获取通过调用 PromptForFamilyInstancePlacement() 方法而新增的族实例,你可以通过注册 DocumentChanged 事件来实现。该事件
的参数 DocumentChangedEventArgs 提供一个 GetAddedElementIds() 方法来获取新增元素的 element ids。需要注意的是:该事件在每新增一个元素时都会触发。
这就意味着如果你在调用 PromptForFamilyInstancePlacement() 方法时每放置了一个族实例,该事件就会被触发一次。如果你希望在 PromptForFamilyInstancePlacement()
方法调用结束时获取所有通过该方法新增的族实例,你就必须自己维护一个集合。并在每次 DocumentChanged 事件被触发时记录下最近新增的族实例。
就像我一直强调的:为 Revit 添加的任何一个事件处理器都会带来额外的系统开销。所以应当尽量减少使用它们。在这个问题中,合理的使用方式是在最靠近调用
PromptForFamilyInstancePlacement() 方法的前后注册和注销事件处理器。
我实现的调用 PromptForFamilyInstancePlacement() 方法的外部命令 CmdPlaceFamilyInstance 的处理过程如下:
1. 使用手动事务模式;
2. 定义一个全局变量 _added_element_ids 来保存所有新增的族实例;
3. 使用元素过滤收集器来获取需要放置实例的族类型(本例中是 door);
4. 注册 DocumentChanged 事件;
5. 调用 PromptForFamilyInstancePlacement() 方法;
6. 注销 DocumentChanged 事件;
7. 报告新增族实例的数量。
代码如下:
[Transaction( TransactionMode.Manual )]
[Regeneration( RegenerationOption.Manual )]
class CmdPlaceFamilyInstance : IExternalCommand
{
List<ElementId> _added_element_ids = new List<ElementId>();
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements )
{
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Application app = uiapp.Application;
Document doc = uidoc.Document;
FilteredElementCollector collector
= new FilteredElementCollector( doc );
collector.OfCategory( BuiltInCategory.OST_Doors );
collector.OfClass( typeof( FamilySymbol ) );
FamilySymbol symbol = collector.FirstElement() as FamilySymbol;
_added_element_ids.Clear();
app.DocumentChanged
+= new EventHandler<DocumentChangedEventArgs>(
OnDocumentChanged );
uidoc.PromptForFamilyInstancePlacement( symbol );
app.DocumentChanged
-= new EventHandler<DocumentChangedEventArgs>(
OnDocumentChanged );
int n = _added_element_ids.Count;
TaskDialog.Show(
"Place Family Instance",
string.Format(
"{0} element{1} added.", n,
( ( 1 == n ) ? "" : "s" ) ) );
return Result.Succeeded;
}
void OnDocumentChanged( object sender, DocumentChangedEventArgs e )
{
_added_element_ids.AddRange( e.GetAddedElementIds() );
}
}