Building Coder(Revit 二次开发) - 放置族实例

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() );
  }
}


你可能感兴趣的:(Building Coder(Revit 二次开发) - 放置族实例)