链接文件

Revit API可以确定Revit中的哪些图元是对外部文件(“链接文件”)的引用,并可以对Revit加载外部文件的方式进行一些修改。

包含ExternalFileReference的元素是引用基.rvt文件之外的某个文件的元素。示例包括Revit链接、CAD链接、存储注释记号文件位置的图元和渲染贴花。IsExternalFileReference()返回元素是否表示外部文件。GetExternalFileReference()返回给定Element的ExternalFileReference,其中包含与该元素引用的外部文件有关的信息。

以下类与Revit API中的链接文件关联:

  • ExternalFileReference - 非Element类,包含Revit项目引用的单个外部文件的路径和类型信息。
  • ExternalFileUtils -一个实用程序类,允许用户查找所有外部文件引用,从元素中获取外部文件引用,或者告诉元素是否是外部文件引用。
  • RevitLinkType -表示链接到Revit项目的Revit文件的图元。
  • ModelPath - 包含文件(不一定是.rvt文件)路径信息的非元素类。路径可以指向本地或网络驱动器上的某个位置,也可以指向Revit Server位置。
  • ModelPathUtils -一个实用程序类,它提供了在字符串和ModelPaths之间进行转换的方法。
  • TransmissionData -一个类,用于存储文档中所有外部文件引用的信息。无需打开文档即可读取Revit项目的TransmissionData。

模型路径

ModelPaths是指向另一个文件的路径。它们可以引用Revit模型,也可以引用Revit的任何外部文件引用(如DWG链接)。路径可以是相对路径,也可以是绝对路径,但它们必须包含指示文件类型的扩展名。相对路径通常相对于当前打开的文档。如果当前文档是工作共享的,则路径将被视为相对于中心模型。若要创建ModelPath,请使用派生类FilePath或ServerPath之一。

ModelPathUtils类包含用于将ModelPaths与用户可见的路径字符串相互转换的实用函数,以及用于确定字符串是否为有效的服务器路径的实用函数。

共享坐标使其他链接模型能够知道一个链接文件的位置。

  • 从指定的链接实例获取项目坐标。这适用于Revit链接(RevitLinkInstance)和DWG链接(ImportInstance)。

  • 将共享坐标发布到指定的ProjectLocation。此方法仅适用于Revit链接。这些只读属性提供有关SiteLocation的地理坐标系的信息。地理坐标系是从AutoCAD或Civil 3D的DWG文件导入的。如果SiteLocation具有地理坐标系信息,则当模型的测量点移动时,SiteLocation的纬度和经度将自动更新。

  • SiteLocation. GeoCoordinateSystemId-获取与地理坐标系ID对应的字符串,例如SiteLocation的”AMG-50”或”Beijing1954/a.GK3d-40”。如果没有为SiteLocation指定坐标系,则该值将为空字符串。

  • GeoCoordinateSystemDefinition -获取描述地理坐标系的XML字符串。如果没有为SiteLocation指定坐标系,则该值将为空字符串。

本节中的页面

  • Revit 链接
  • 管理外部文件

Revit 链接

Revit文档可以包含指向各种外部文件(包括其他Revit文档)的链接。Revit API中的这些链接类型由RevitLinkType和RevitLinkInstance类表示。RevitLinkType类表示引入到当前文档(“主体”)中的另一个Revit文档(“链接”),而RevitLinkInstance类表示RevitLinkType的实例。

创建新链接

要创建新的Revit链接,请使用静态RevitLinkType.Create()方法(该方法将创建新的Revit链接类型并加载链接文档)和静态RevitLinkInstance.Create()方法(将链接的实例放置在模型中)。RevitLinkType.Create()方法需要一个文档(将作为宿主)、要链接的文件的ModelPath和RevitLinkOptions对象。RevitLinkOptions类表示用于创建和加载Revit链接的选项。选项包括Revit是否存储链接文件和工作集配置的相对路径或绝对路径。WorksetConfiguration类用于指定创建链接时将打开哪些工作集(如果有)。请注意,相对路径或绝对路径决定了Revit存储路径的方式,但传递到Create()方法中的ModelPath需要完整的路径才能最初查找链接的文档。

下面的示例演示RevitLinkType.Create()的用法。变量pathName是磁盘上要链接的文件的完整路径。

代码区域:创建新的Revit链接

1
2
3
4
5
6
7
8
public ElementId CreateRevitLink(Document doc, string pathName)
{
FilePath path = new FilePath(pathName);
RevitLinkOptions options = new RevitLinkOptions(false);
// Create new revit link storing absolute path to file
LinkLoadResult result = RevitLinkType.Create(doc, path, options);
return (result.ElementId);
}

创建RevitLinkType后,可以将实例添加到文档中。在下面的示例中,添加了两个RevitLinkType实例,偏移量为100 ‘。在创建RevitLinkInstance之前,Revit链接将显示在“管理链接”窗口中,但链接文件的图元在任何视图中都不可见。

代码区域:创建新的Revit Link实例

1
2
3
4
5
6
7
8
9
public void CreateLinkInstances(Document doc, ElementId linkTypeId)
{
// Create revit link instance at origin
RevitLinkInstance.Create(doc, linkTypeId);
RevitLinkInstance instance2 = RevitLinkInstance.Create(doc, linkTypeId);
// Offset second instance by 100 feet
Location location = instance2.Location;
location.Move(new XYZ(0, -100, 0));
}

上面的例子使用本地磁盘上的文件。下面是一个更复杂的示例,涉及到Revit服务器上模型的链接。

代码区域:创建指向Revit服务器上模型的新Revit链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
public static void CreateLinkToServerModel(UIApplication uiApp)
{
UIDocument uiDoc = uiApp.ActiveUIDocument;
Document doc = uiDoc.Document;

// Try to get the server path for the particular model on the server
Application application = uiApp.Application;
String hostId = application.GetRevitServerNetworkHosts().First();
String rootFolder = "|";
ModelPath serverPath = FindWSAPIModelPathOnServer(application, hostId, rootFolder, "Wall pin model for updaters.rvt");

using (Transaction t = new Transaction(doc, "Create link"))
{
t.Start();
RevitLinkOptions options = new RevitLinkOptions(false);

LinkLoadResult result = RevitLinkType.Create(doc, serverPath, options);

RevitLinkInstance.Create(doc, result.ElementId);
t.Commit();
}
}

private static ModelPath FindWSAPIModelPathOnServer(Application app, string hostId, string folderName, string fileName)
{
// Connect to host to find list of available models (the "/contents" flag)
XmlDictionaryReader reader = GetResponse(app, hostId, folderName + "/contents");
bool found = false;

// Look for the target model name in top level folder
List folders = new List();
while (reader.Read())
{
// Save a list of subfolders, if found
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Folders")
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Folders")
break;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
{
reader.Read();
folders.Add(reader.Value);
}
}
}
// Check for a matching model at this folder level
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Models")
{
found = FindModelInServerResponseJson(reader, fileName);
if (found)
break;
}
}

reader.Close();

// Build the model path to match the found model on the server
if (found)
{
// Server URLs use "|" for folder separation, Revit API uses "/"
String folderNameFragment = folderName.Replace('|', '/');

// Add trailing "/" if not present
if (!folderNameFragment.EndsWith("/"))
folderNameFragment += "/";

// Build server path
ModelPath modelPath = new ServerPath(hostId, folderNameFragment + fileName);
return modelPath;
}
else
{
// Try subfolders
foreach (String folder in folders)
{
ModelPath modelPath = FindWSAPIModelPathOnServer(app, hostId, folder, fileName);
if (modelPath != null)
return modelPath;
}
}

return null;
}

// This string is different for each RevitServer version
private static string s_revitServerVersion = "/RevitServerAdminRESTService2014/AdminRESTService.svc/";

private static XmlDictionaryReader GetResponse(Application app, string hostId, string info)
{
// Create request WebRequest request = WebRequest.Create("http://" + hostId + s_revitServerVersion + info);
request.Method = "GET";

// Add the information the request needs

request.Headers.Add("User-Name", app.Username);
request.Headers.Add("User-Machine-Name", app.Username);
request.Headers.Add("Operation-GUID", Guid.NewGuid().ToString());

// Read the response
XmlDictionaryReaderQuotas quotas =
new XmlDictionaryReaderQuotas();
XmlDictionaryReader jsonReader =
JsonReaderWriterFactory.CreateJsonReader(request.GetResponse().GetResponseStream(), quotas);

return jsonReader;
}

private static bool FindModelInServerResponseJson(XmlDictionaryReader reader, string fileName)
{
// Read through entries in this section
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Models")
break;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
{
reader.Read();
String modelName = reader.Value;
if (modelName.Equals(fileName))
{
// Match found, stop looping and return
return true;
}
}
}

return false;
}

在下面的示例中,将获取WorksetConfiguration并对其进行修改,以便在创建新链接之前仅打开一个指定的工作集并将其设置回RevitLinkOptions。

代码区域:创建打开一个工作集的链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public bool CreateRevitLinkWithOneWorksetOpen(Document doc, string pathName, string worksetName)
{
FilePath path = new FilePath(pathName);
RevitLinkOptions options = new RevitLinkOptions(true);

// Get info on all the user worksets in the project prior to opening
IList worksets = WorksharingUtils.GetUserWorksetInfo(path);
IList worksetIds = new List();
// Find worksetName
foreach (WorksetPreview worksetPrev in worksets)
{
if (worksetPrev.Name.CompareTo(worksetName) == 0)
{
worksetIds.Add(worksetPrev.Id);
break;
}
}

// close all worksets but the one specified
WorksetConfiguration worksetConfig = new WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets);
if (worksetIds.Count > 0)
{
worksetConfig.Open(worksetIds);
}
options.SetWorksetConfiguration(worksetConfig);

LinkLoadResult result = RevitLinkType.Create(doc, path, options);
RevitLinkType type = doc.GetElement(result.ElementId) as RevitLinkType;
return (result.LoadResult == LinkLoadResultType.LinkLoaded);
}

无论是创建还是加载链接,都将返回LinkLoadResult。此类有一个属性来确定链接是否已加载。它还有一个ElementId属性,该属性是创建或加载的链接模型的ID。 RevitLinkInstance.Create(ImportPlacement放置)创建链接的Revit项目的新实例(RevitLinkType)。根据输入放置类型,将按原点到原点或按共享坐标放置图形。 #加载和卸载链接 RevitLinkType有几种与加载链接相关的方法。Load()、LoadFrom()和Unload()允许加载或卸载链接,或者从新位置加载链接。这些方法重新生成文档。文档的撤消历史记录将通过这些方法清除。所有显式启动的事务阶段(例如事务、事务组和子事务)必须在调用这些方法之前完成。 静态方法RevitLinkType.IsLoaded()将返回链接是否已加载。 #获取链接信息 文档中的每个RevitLinkType都可以有一个或多个关联的RevitLinkType。RevitLinkInstance.GetLinkDocument()方法返回与Revit链接关联的Document。不能修改此单据,这意味着不能执行需要交易或修改单据在内存中状态的操作(如保存和关闭)。 可以使用从RevitLinkInstance.GetTypeId()方法获得的ElementId从文档中检索RevitLinkInstance的关联RevitLinkType。链接文件的RevitLinkType有几个与嵌套链接相关的方法和属性。链接到另一个文档的文档本身可能具有链接。如果RevitLinkType是嵌套链接(即,它有其他链接作为父链接),IsNested属性返回true;如果它是顶级链接,IsNested属性返回false。方法GetParentId()将获取此链接的直接父级的ID,而GetRootId()将返回此链接最终链接的顶级链接的ID。如果此链接是顶级链接,则这两个方法都将返回invalidElementId。方法GetChildIds()将返回所有直接链接到此链接的链接的元素ID。 例如,如果C链接到文档B,而B又链接到文档A,则为C链接调用GetParentId()将返回文档B的ID,为C链接调用GetRootId()将返回文档A的ID。为文档A调用GetChildIds()将只返回B的链接的id,因为C不是A下的直接链接。 RevitLinkType还有一个PathType属性,该属性指示外部文件参照的路径是相对于主体文件的位置(或相对于中心模型的位置(如果主体是工作共享的)),还是相对于磁盘或网络上某个位置的绝对路径,或者路径是相对于Revit Server位置。 RevitLinkType的AttachmentType属性指示链接是附件还是覆盖。“附件”链接被认为是其父链接的一部分,如果其父链接到另一个文档中,“附件”链接将被沿着。“覆盖”链接只有在直接打开父链接时才可见。 下面的示例获取文档中的所有RevitLinkList并显示有关它们的一些信息。

代码区域:获取链接信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void GetAllRevitLinkInstances(Document doc)
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfClass(typeof(RevitLinkInstance));
StringBuilder linkedDocs = new StringBuilder();
foreach (Element elem in collector)
{
RevitLinkInstance instance = elem as RevitLinkInstance;
Document linkDoc = instance.GetLinkDocument();
linkedDocs.AppendLine("FileName: " + Path.GetFileName(linkDoc.PathName));
RevitLinkType type = doc.GetElement(instance.GetTypeId()) as RevitLinkType;
linkedDocs.AppendLine("Is Nested: " + type.IsNestedLink.ToString());
}

TaskDialog.Show("Revit Links in Document", linkedDocs.ToString());
}

链接几何图形

共享坐标

RevitLinkType方法SavePositions()和HasSaveablePositions()支持将共享坐标更改保存回链接文档。使用HasSaveablePositions()确定链接是否具有可以保存的共享定位更改。调用SavePositions()将共享坐标更改保存回链接文档。SavePositions()需要ISaveSharedCoordinatesCallback接口的实例,以解决Revit遇到已修改链接时的情况。接口的GetSaveModifiedLinksOption()方法确定Revit是应保存链接、不保存链接还是完全放弃共享定位。

虽然SavePositions()不清除文档的撤消历史记录,但它不能撤消,因为它将链接的共享坐标更改保存到磁盘。

几何参照的转换

Reference类具有与链接文件相关的成员,这些成员允许在仅引用链接内容的Reference对象和引用宿主的Reference对象之间进行转换。例如,这允许应用程序查看链接中的几何图形,查找所需的面,并将该面的参照转换为主体中适合用于放置基于面的实例的参照。此外,这些Reference成员还可以在主体中获取引用(例如,从尺寸或族中),并将其转换为链接中的引用,以便在Element.GetGeometryObjectFromReference()中使用。

Reference.LinkedElementId属性表示此引用所引用的链接文档中顶级元素的ID,或InvalidElementId表示未引用链接RVT文件中元素的引用。Reference.RightLinkReference()方法使用RevitLinkInstance从Revit链接中的参照创建Reference和Reference.ReferenceInLink()方法可从宿主文件中的引用创建Revit链接中的引用

拾取链接中的元素

选择方法PickObject()和PickObjects()允许选择Revit链接中的对象。若要允许用户选择链接文件中的元素,请对PickObject()或PickObjects()的第一个参数使用ObjectType.LinkedElement枚举值。请注意,此选项仅允许选择链接中的元素,而不允许选择宿主文档中的元素。

在下面的示例中,ISelectionFilter用于仅允许在链接文件中选择墙。

代码区域:选择链接文件中的元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public void SelectElementsInLinkedDoc(Autodesk.Revit.DB.Document document)
{
UIDocument uidoc = new UIDocument(document);
Selection choices = uidoc.Selection;
// Pick one wall from Revit link
WallInLinkSelectionFilter wallFilter = new WallInLinkSelectionFilter();
Reference elementRef = choices.PickObject(ObjectType.LinkedElement, wallFilter, "Select a wall in a linked document");
if (elementRef != null)
{
TaskDialog.Show("Revit", "Element from link document selected.");
}
}

// This filter allows selection of only a certain element type in a link instance.
class WallInLinkSelectionFilter : ISelectionFilter
{
private RevitLinkInstance m_currentInstance = null;

public bool AllowElement(Element e)
{
// Accept any link instance, and save the handle for use in AllowReference()
m_currentInstance = e as RevitLinkInstance;
return (m_currentInstance != null);
}

public bool AllowReference(Reference refer, XYZ point)
{
if (m_currentInstance == null)
return false;

// Get the handle to the element in the link
Document linkedDoc = m_currentInstance.GetLinkDocument();
Element elem = linkedDoc.GetElement(refer.LinkedElementId);

// Accept the selection if the element exists and is of the correct type
return elem != null && elem is Wall;
}
}

管理外部文件

ExternalFileUtils

顾名思义,这个实用程序类提供有关外部文件引用的信息。ExternalFileUtils.GetAllExternalFileReferences()方法返回文档中作为外部文件引用的所有元素的ElementId集合。(Note它不会返回嵌套的Revit链接的ID;它只返回顶级参照。)此实用程序类还有另外两个方法,IsExternalFileReference()和GetExternalFileReference(),它们执行与Element类的类似命名方法相同的功能,但可以在您拥有ElementId而不是首先获取Element时使用。

TransmissionDataTransmissionData存储有关外部文件引用的先前状态和请求状态的信息。这意味着它存储最近打开此TransmissionData文档时的引用的加载状态和路径。它还存储加载状态和路径信息,以便Revit在下次打开文档时执行操作。

因此,可以使用TransmissionData对外部文件参照执行操作,而无需打开整个关联的Revit文档。方法ReadTransmissionData和WriteTransmissionData可用于获取有关外部引用的信息,或更改该信息。例如,使用TransmissionData对象调用WriteTransmissionData,该对象已将所有引用设置为LinkedFileStatus.Unloaded,这将导致下次打开文档时不加载任何引用。

TransmissionData无法添加或删除对外部文件的引用。如果使用与外部文件引用元素不对应的ElementId调用AddExternalFileReference,则文件加载时将忽略该信息。

以下示例读取给定位置的文件的TransmissionData,并将所有Revit链接设置为在下次打开文档时卸载。

代码区域:卸载Revit链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void UnloadRevitLinks(ModelPath location)
/// This method will set all Revit links to be unloaded the next time the document at the given location is opened.
/// The TransmissionData for a given document only contains top-level Revit links, not nested links.
/// However, nested links will be unloaded if their parent links are unloaded, so this function only needs to look at the document's immediate links.
{
// access transmission data in the given Revit file
TransmissionData transData = TransmissionData.ReadTransmissionData(location);
if (transData != null)
{
// collect all (immediate) external references in the model
ICollection externalReferences = transData.GetAllExternalFileReferenceIds();
// find every reference that is a link
foreach (ElementId refId in externalReferences)
{
ExternalFileReference extRef = transData.GetLastSavedReferenceData(refId);
if (extRef.ExternalFileReferenceType == ExternalFileReferenceType.RevitLink)
{
// we do not want to change neither the path nor the path-type
// we only want the links to be unloaded (shouldLoad = false)
transData.SetDesiredReferenceData(refId, extRef.GetPath(), extRef.PathType, false);
}
}

// make sure the IsTransmitted property is set
transData.IsTransmitted = true;
// modified transmission data must be saved back to the model
TransmissionData.WriteTransmissionData(location, transData);
}
else
{
Autodesk.Revit.UI.TaskDialog.Show("Unload Links", "The document does not have any transmission data");
}
}

为服务器上的位置构造ModelPath

要读取TransmissionData对象,需要调用静态方法TransmissionData.ReadTransmissionData。它需要一个ModelPath对象。

有两种方法可以构造引用中心文件的ModelPath对象。第一种方法涉及使用ModelPathUtils和基本ModelPath类。其步骤如下:

  1. 编写中心文件的用户可见路径字符串:ModelPathUtils.GetRevitServerPrefix()+“相对路径”。

    注意:“相对路径”中使用的文件夹分隔符是正斜杠(/)。正确的分隔符是正斜杠。

  2. 通过ModelPathUtils.ConvertUserVisiblePathToModelPath()方法创建ModelPath对象。传入在上一步中编写的字符串。

  3. 通过TransmissionData::ReadTransmissionData()方法读取传输数据。传入在上一步中获得的ModelPath。

以下示例演示了此方法,假定中心文件testmodel.rvt存储在Revit Server的根文件夹SHACNG 035 WQRP中。

代码区域:使用ModelPath构造中心文件的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class RevitCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string messages, ElementSet elements)
{
UIApplication app = commandData.Application;
Document doc = app.ActiveUIDocument.Document;
Transaction trans = new Transaction(doc, "ExComm");
trans.Start();
string visiblePath = ModelPathUtils.GetRevitServerPrefix() + "/testmodel.rvt";
ModelPath serverPath = ModelPathUtils.ConvertUserVisiblePathToModelPath(visiblePath);
TransmissionData transData = TransmissionData.ReadTransmissionData(serverPath);
string mymessage = null;
if (transData != null)
{
//access the data in the transData here.
}
else
{
Autodesk.Revit.UI.TaskDialog.Show("Unload Links",
"The document does not have any transmission data");
}
trans.Commit();
return Result.Succeeded;
}
}

构造引用中心文件的ModelPath对象的第二种方法是使用子类ServerPath。如果程序知道本地服务器名称,则可以使用此方法,但不建议使用此方法,因为Revit用户可以从Revit UI更改服务器名称。其步骤如下: 1. 使用ServerPath构造函数创建ServerPath对象。

1
new ServerPath(“ServerNameOrServerIp”, “relative path without the initial forward slash”).

注意:第一个参数是服务器名称,而不是ModelPathUtils.GetRevitServerPrefix()返回的字符串,第二个参数不包括首个正斜杠。请参见下面的示例代码。文件夹分隔符也是一个正斜杠(/)。 1. 通过TransmissionData.ReadTransmissionData()方法读取TransmissionData对象。 下面的代码演示了此方法。

代码区域:使用ServerPath构造中心文件的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class RevitCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData,
ref string messages, ElementSet elements)
{
UIApplication app = commandData.Application;
Document doc = app.ActiveUIDocument.Document;
Transaction trans = new Transaction(doc, "ExComm");
trans.Start();
ServerPath serverPath = new ServerPath("SHACNG035WQRP", "testmodel.rvt");
TransmissionData transData = TransmissionData.ReadTransmissionData(serverPath);
string mymessage = null;
if (transData != null)
{
//access the data in the transData here.
}
else
{
Autodesk.Revit.UI.TaskDialog.Show("Unload Links",
"The document does not have any transmission data");
}
trans.Commit();
return Result.Succeeded;
}
}

注:翻译自Revit API Developers Guide