今天瞎折腾的时候,突然在Revit Document里看到一组相当有趣并有着详细注释的API:
- Document..::..MakeTransientElements The method establishes a context within which transient elements will be created and then invokes the given maker object to create the elements. For more information refer to the IsTransient method.
- Element..::..IsTransient Transient elements are usually created for short term use. This type of element can be created via Document.MakeTransientElements(). Transient and Permanent elements are not allowed to reference each other. A transient element can only refer to other transient elements in the same document. Transient elements also cannot be selected or scheduled, and will not be saved when the document is saved, and will be discarded when the document is closed. Modifying a transient element does not require a transaction, because such elements are not part of the model. As an effect of this, however, creation and modification of transients cannot be undone. Because transient elements are technically not part of the model, they will not be found when using standard element filters, or in any collection of elements Revit returns, such as elements reported in dynamic updaters, etc.
看上去像是能生成出一些不可选中,只可作为参考的图元,好奇的我作了一番探索.
初步尝试
public class GenerateTransientElement : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
Document document = commandData.Application.ActiveUIDocument.Document;
document.MakeTransientElements(new TransientElementMaker(document));
return Result.Succeeded;
}
}
public class TransientElementMaker : ITransientElementMaker
{
private readonly Document _document;
public TransientElementMaker(Document document)
{
_document = document;
}
public void Execute()
{
DirectShape ds = DirectShape.CreateElement(_document, new ElementId(BuiltInCategory.OST_GenericModel));
//CreateUtils.CreateCube(), CreateUtils.CreateSphere()为创建一个立方体与球体Solid,文略
ds.AppendShape(new List<GeometryObject>() { CreateUtils.CreateCube(), CreateUtils.CreateSphere(new XYZ(0, 0, 15), 5) });
}
}
然而执行完代码什么事情都没发生,正当我以为自己使用姿势不对的时候,一个误点击突然让它们显现出来:
又经过一番尝试之后,发现Transient Elements有着如下特点:
- 在View3D中执行代码后,点击任一墙体(我尝试其他类型图元不行,天知道为什么)后,代码中相关的图元会被渲染出来
- 图元不可选中,只有线框
- 切换视图并不会使其消失,但使用任一Command都会(切换了UI上下文?)
嗯...很Transient.更让我意想不到的是,Google了一番后发现了来自官方的吐槽:
This seems to be 'half-finished' work that should not have been exposed to public API.
It looks like it never worked reliably and will probably be removed again.
So, the short answer to users: please do not use it.
这你也好意思保留了这API起码四年??
再次探索
我突然想起了Dynamo的Background 3D Preview功能,那就说明这种临时图元是确实可以存在的,挖了一番源码后的Demo如下:
public class GenerateTransientElement : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
Document document = commandData.Application.ActiveUIDocument.Document;
using (Transaction transaction = new Transaction(document, "display"))
{
transaction.Start();
IEnumerable<GeometryObject> geoms = new List<GeometryObject>() { CreateUtils.CreateCube(),CreateUtils.CreateSphere(new XYZ(0, 0, 15), 5) };
var method = GenerateTransientDisplayMethod();
var argsM = new object[4];
argsM[0] = document;
argsM[1] = ElementId.InvalidElementId;
argsM[2] = geoms;
argsM[3] = ElementId.InvalidElementId;
var transientElementId = (ElementId)method.Invoke(null, argsM);
transaction.Commit();
}
return Result.Succeeded;
}
/// <summary>
/// Invoke internal method by Reflection
/// </summary>
/// <returns></returns>
internal static MethodInfo GenerateTransientDisplayMethod()
{
var geometryElementType = typeof(GeometryElement);
var geometryElementTypeMethods =
geometryElementType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
var method = geometryElementTypeMethods.FirstOrDefault(x => x.Name == "SetForTransientDisplay");
return method;
}
}
这种Transient Elements的特点:
- 图元依旧是不可选中
- Element.IsTransient属性为True
- 给相关的GeometryObject加上Material会生效
- 任何手动操作都无法删除这个图元,除非在代码中使用Document.Delete(transientElementId) 或重新打开此文档(与Dynamo一致)
果然是一家人,API都提供内部版本的(逃.
完.
how to get transientElementId?