Here is pretty thorough exploration by Joe Ye on how to retrieve linked Revit files in a project.

The built-in category of a linked Revit file is BuiltInCategory.OST_RvtLinks, and the class storing it in the Revit database is Instance.
Therefore, we can retrieve all linked Revit files in a document like this:


Autodesk.Revit.Creation.Filter cf
  = app.Create.Filter;
 
BuiltInCategory bic
  = BuiltInCategory.OST_RvtLinks;
 
Filter f1
  = cf.NewCategoryFilter( bic );
 
Filter f2
  = cf.NewTypeFilter( typeof( Instance ) );
 
Filter f3
  = cf.NewLogicAndFilter( f1, f2 );
 
List<Element> links = new List<Element>();
 
Document doc = app.ActiveDocument;
doc.get_Elements( f3, links );

We simply set up filters for the category and class, which is also the System.Type, combine them in a Boolean expression, and ask the document for the elements matching this filter.

Revit 2008 did not provide a valid category property for the linked model, and at that time the API did not support filtering for specific elements, so one had to use a slower and more complex approach such as this:


BuiltInParameter bip
  = BuiltInParameter.ELEM_TYPE_PARAM;
 
Document doc = app.ActiveDocument;
ElementIterator it = doc.Elements;
 
List<Element> links = new List<Element>();
 
while( it.MoveNext() )
{
  Instance inst = it.Current as Instance;
  if( null != inst )
  {
    Parameter p = inst.get_Parameter( bip );
    ElementId id = p.AsElementId();
    Element e = doc.get_Element( ref id );
    string n = e.Name;
    string s = n.Substring( n.Length - 4 );
    if( s.ToLower().Equals( ".rvt" ) )
    {
      links.Add( inst );
    }
  }
}

In this loop we check all elements whose class is Instance.
On those, we check for the built-in parameter ELEM_TYPE_PARAM, which gives us the family.
The parameter is an element id.
With the id, we can ask the document to retrieve the element ‘e’ and ask it for its name ‘n’.
The linked file’s element name is the file name, which ends in “.rvt”.

Linked File Path

This quickly leads to a new question: Having the file name, is it possible to also get the file path for a given Revit link?

For that, we can make use of the fact that Application.Documents manages all imported Revit files.
The linked Revit files’ full path can be retrieved from the Document.PathName property.
Here is some code to set up a dictionary mapping each Revit link’s file name to its full path:


DocumentSet docs = app.Documents;
int n = docs.Size;
 
Dictionary<string, string> dict
  = new Dictionary<string, string>( n );
 
foreach( Document doc in docs )
{
  string path = doc.PathName;
  int i = path.LastIndexOf( "\" ) + 1;
  string name = path.Substring( i );
  dict.Add( name, path );
}

Unfortunately, the question still has some thorny bits in it, because it remains unclear how to get the linked files in a Revit project when there are two open projects, both referencing a files with the
same name are but at different paths. For example:

  • Open a Revit project: C:tmpalink.rvt with a link to C:tmpax.rvt.
  • Open another Revit project in the same Revit session: C:tmpbx.rvt.

Now we want the Revit links for the project “link.rvt”.
From the elements collection we can find the name of the linked file “x.rvt”.
But there are two files with the same name in the documents collection and we don’t know which one of them to choose.
The files in the documents collection are:

  • C:tmpalink.rvt
  • C:tmpax.rvt
  • C:tmpbx.rvt

For this situation, you can distinguish between the opened Revit document and the imported one.
When you retrieve a document from Application.Documents and it is currently open in Revit, its property Document.ActiveView is not null.
If the document is imported, its Document.ActiveView is null.
This can be used to tell which one is imported.

Unfortunately, this still does not solve the problem if two opened documents import two Revit files with the same file name in different folders.

Here is the mainline of an external command making use of these methods:


Application app = commandData.Application;
 
Dictionary<string, string> dict
  = GetFilePaths( app );
 
List<Element> links
  = GetLinkedFiles( app );
 
int n = links.Count;
Debug.WriteLine( string.Format(
  "There {0} {1} linked Revit model{2}.",
  (1 == n ? "is" : "are"), n,
  Util.PluralSuffix( n ) ) );
 
string name;
char[] sep = new char[] { ':' };
string[] a;
 
foreach( Element link in links )
{
  name = link.Name;
  a = name.Split( sep );
  name = a[0].Trim();
 
  Debug.WriteLine( string.Format(
    "Link '{0}' full path is '{1}'.",
    name, dict[name] ) );
}
return CmdResult.Succeeded;

It makes use of the fact that the link instance element names use ” : ” to delimit the file name, as in:


"two_rooms.rvt : 2 : location "

Here is the output from running this command in a simple sample file:


There are 2 linked Revit models.
Link 'roof.rvt' full path is 'C:tmproof.rvt'.
Link 'two_rooms.rvt' full path is 'C:tmptwo_rooms.rvt'.

Joe suggests that yet another situation also needs to be considered. Assuming the following situation:

  • C:tempafile1.rvt is opened.
  • C:tempamain.rvt is also opened.
  • C:tempbfile1.rvt is imported into main.rvt.

In this case, the GetFilePaths method retrieves two file paths:

  1. C:tempafile1.rvt
  2. C:tempbfile1.rvt

From the link name file1.rvt alone, we cannot tell which path is the imported one.
To do so, we need to remove the non-imported files from the mapping.
In this case, GetFilePaths should remove the opened Revit file and only contain non-opened ones, which represent the imported files.
This can be achieved by using the ActiveView property, for instance by enhancing GetFilePaths like this:


Dictionary<string,string> GetFilePaths(
  Application app,
  bool onlyImportedFiles )
{
  DocumentSet docs = app.Documents;
  int n = docs.Size;
 
  Dictionary<string, string> dict
    = new Dictionary<string, string>( n );
 
  foreach( Document doc in docs )
  {
    if( !onlyImportedFiles
      || ( null == doc.ActiveView ) )
    {
      string path = doc.PathName;
      int i = path.LastIndexOf( "\" ) + 1;
      string name = path.Substring( i );
      dict.Add( name, path );
    }
  }
  return dict;
}

I hope that this discussion is useful for exploring situations requiring handling of linked files in Revit.
Many thanks to Joe for his thorough exploration!

Here is
version 1.0.0.17
of the complete Visual Studio solution with the new command CmdLinkedFiles discussed here.


Comments

25 responses to “Linked Files”

  1. Hi Jeremy, i am currently working with revit links and am having trouble getting the location of the link (i.e. its x,y,z “insertion” point). ADN inform me that it is not currently supported, but i was hoping someone out there might have a creative way of retrieving this information?

  2. Dear Krispy,
    Yes, I noticed your case and followed it with interest. If I knew a good answer, I would have piped up. One further thing you might try is to raise the issue with other developers in the discussion group at http://discussion.autodesk.com > Revit API … there are some pretty knowledgeable and creative spirits out there. Please let us know if you find a solution :-) Good luck and happy holidays!
    Jeremy

  3. Jeremy,
    I have a quick question. I’m trying to make a command to turn off the visibility of linked revit (and dwg) models in the current view.
    For DWG I simply loop through doc.settings.categories and if it ends with .dwg i make it invisible in the current view.
    However this does not work for RVT, so I had a look with rvtmgdbg and it says that my linked file on the plan is part of the OST_Rvtlinks build in category.
    So I changed my code to do:
    //get category for linked files
    Category linkedRevitCat = _doc.Settings.Categories.get_Item(BuiltInCategory.OST_RvtLinks);
    //loop through all categories in document
    foreach (Category c in _doc.Settings.Categories) {
    //if they end with dwg or rvt set them as !curvisibility in current view
    if (c.Name.ToLower().EndsWith(“.dwg”) || c.Name.ToLower().Contains(“.rvt”) ||
    c.Name.ToLower().EndsWith(“.dwf”)
    || c.Name.ToLower().EndsWith(“.dxf”) || c.Name.ToLower().EndsWith(“.dwfx”) ||
    (linkedRevitCat != null &&c.Id.Equals(linkedRevitCat.Id)))
    //set visibility to be opposite of what tehy are now
    _app.ActiveDocument.ActiveView.setVisibility(c, !c.get_Visible(_app.ActiveDocument.ActiveView));
    }
    unfortunately, linkedRevitCat always is null, and when I run the elements filter api sample, ost_rvtlinks doesnt come up as a category list?

  4. Hi Rod,
    Thank you for the interesting question and for the positive information on how simple it is to hide the linked DWG files. I am looking the OST_RvtLinks issue. Right now I am on the road, however, giving Revit API training in Barcelona, so it might be a while before I am able to find a resolution.
    Cheers, Jeremy.

  5. Hi again Rod,
    Here are some ideas on this topic:
    http://thebuildingcoder.typepad.com/blog/2009/01/hiding-linked-files.html
    Cheers, Jeremy.

  6. hi,
    Do you konw how to get the links instance’s Rotation,
    For example,
    I create project A, the links to project B,
    then rotate the instance of Project B.
    I want to get the rotation of project B,
    because there is no way to get the elements right geometry inside instance.

  7. Hi Ben,
    I had a quick look at an Instance element. I would have assumed that we could get some information from its Location property, but unfortunately that is not the case. So sorry, I don’t know offhand.
    Cheers, Jeremy.

  8. Hi Jeremy,
    Thanks for your replay so quickly,
    It seems that there has no chance to get the right position and rotation from Revit API.
    I had searched it form internet, but nothing for help.
    Maybe Revit API does provide this functionality.
    Thanks again!
    Regards Ben.

  9. Hi Ben,
    Just to be completely sure and wrap this up, I had a chat with the development team about this. They confirm that there is no known way to determine the rotation of an instance of a linked file via the current API. We will consider API wish to expose the Transform information of the Revit.Elements.Instance.
    Cheers, Jeremy.

  10. Hi Jeremy,
    Thanks!
    Here is the backgroud about this question,
    the nesting links, for example,
    project A Links B Links C,
    then open project A, can’t get the Instance C positon inside B, only the position in A,
    instance.get_BoundingBox(activeView), this method can only get the BoundingBox in project C. Imposibble to get the BoundingBox inside B.
    In AutoCAD , it provide one method to loop objects of the reference project: entity.explode .
    and its position is the right position.
    so , I’m wondering what RevitAPI doesn’t provide it.
    Regares
    Ben

  11. PS: Does the Revit api provide the bind method ?
    In Revit UI , user can bind the Links instance.
    But I can’t find the method from API.

  12. Dear Ben,
    I do not completely understand you question, except that you are trying to determine the position of nested linked files. I do believe that this can be achieved using the BuondingBox property, as you seem to be attempting to do. I will gladly explore this issue in a future post.
    Regarding the binding of a linked instance, the Revit API currently does not support the binding or importing of linked files.
    Cheers, Jeremy.

  13. Hi Jeremy,
    I want to draw detail arcs on active document using revitapi on multiple levels.
    At present, I am using following method to draw curve:
    region Draw Circle
    // Create an arc which is placed on the plane and whose center is the plane’s origin
    XYZ norm;
    if (end0.X == end1.X)
    {
    norm = XYZ.BasisZ;
    }
    else if (end0.Y == end1.Y)
    {
    norm = XYZ.BasisZ;
    }
    else
    {
    norm = XYZ.BasisZ;
    }
    double startAngle = 0; // The unit is radian
    double endAngle = 2 * Math.PI; // this arc is a circle
    double radiusOfArc = geomArc.Radius;
    Autodesk.Revit.Geometry.Plane objPlane = new Plane(norm, doorPt);
    //drawing the arc of the circle
    Arc arcCircle = revitApp.Create.NewArc(objPlane, radiusOfArc, startAngle, endAngle);
    DetailArc detailArcCircle = doc.Create.NewDetailCurve(doc.ActiveView, arcCircle) as DetailArc;
    I want to draw detail arc on different levels but the above code draws the curves on active level of active view of document.
    How can I accomplish it? Please suggest.
    Regards,
    Shifali

  14. Dear Shifali,
    Once again, I suspect this is an issue that first needs some clarification on the user interface level before attempting to address it through the API.
    Question: is it possible at all in Revit to create a detail curve that appears on multiple levels?
    I suspect that a detail curve is associated with one single view and thus with one single level. If you wish to display the same detail curve in multiple views or levels, you may possibly have to create separate instances of the curve for each view and level. Not being a user interface expert or even knowledgeable in this area, I don’t know the answer to this. Please check that first and let us know the result. Thank you!
    For instance, in the Revit MEP user interface help file HelpMEPENU.chm, I see the following note in the section on detail lines:
    “Detail Lines: The Detail Line tool creates detail lines for detailing drawings. Detail lines are visible only in the view in which they are drawn… Note: If you want to sketch lines that exist in 3D space and display in all views, see Model Lines.”
    Cheers, Jeremy.

  15. Dear Jeremy,
    I really thank you for your reply.
    Actually, I am not finding option in Revit Architecture User Interface to draw a detail arc.
    In my above code, I amm passing the z-coordinate of arc equal to height of level, It should draw the arc on that particular level but i guess the “doc.activeview” in statement:
    “DetailArc detailArcCircle = doc.Create.NewDetailCurve(doc.ActiveView, arcCircle) as DetailArc;”
    is overriding this.
    how should I resolve this issue in my program?
    Thanks & Regards,
    Shifali

  16. Dear Shifali,
    Well, I can find the detail arc in the user interface, and if I can, then everybody can :-)
    Ribbon > Annotate > Detail Line > Draw.
    I believe the Z coordinate is completely ignored when you draw a detail arc, so that will almost definitely not do anything to place the arc on a specific level.
    The only thing you can specify is the view, and that will determine the level as well, won’t it?
    Cheers, Jeremy.

  17. Dear Jeremy,
    I found the Revit UI option of detail arc, thank you so much.
    I am passing the active view of document as parameter, no doubt it determines the level also but only active level in the document not the one particular level i want.
    Regards,
    Shifali

  18. Dear Shifali,
    I am glad you found the detail curve in the user interface. What a relief :-)
    You need to set the first argument of NewDetailCurve() method to the specific level’s ViewPlan. For example, to draw an arc in the second level, first find the ViewPlan instance that corresponds to the second level. You can iterate over all ViewPlan instances and check whether its GenLevel property matches the required level:
    // ViewPlan of “Level 2”
    ViewPlan vp2 = null;
    ElementIterator ei = doc.get_Elements( typeof( ViewPlan ) );
    while( ei.MoveNext() )
    {
    ViewPlan vp = ei.Current as ViewPlan;
    if( vp.GenLevel.Name.Equals( “Level 2” ) )
    {
    vp2 = vp;
    break;
    }
    }
    if( null == vp2 )
    {
    vp2 = doc.ActiveView;
    }
    Arc arc = app.Create.NewArc( objPlane, radiusOfArc, startAngle, endAngle );
    DetailArc detArc = doc.Create.NewDetailCurve( vp2, arc ) as DetailArc;
    Cheers, Jeremy.

  19. Andras Kiss Avatar
    Andras Kiss

    “We will consider API wish to expose the Transform information of the Revit.Elements.Instance.”
    Hi Jeremy,
    Do you happen to know if this issue is resolved in the present API (2010 or 2011)?
    Andras

  20. Dear Andras,
    Are you an ADN member? The ADN DevHelp Online would be a much more appropriate place to ask such a question. Anyway, what I can tell you here and now is that I do see that some work has been done to “expose API to read the instance’s transform”. However, as always, I cannot make any statements whatsoever about anybody’s future products. After all, who knows?
    Cheers, Jeremy.

  21. Hi Jeremy
    Love your work and has been been very helpfull.
    One question on the original topic of RvtLinks, which is now a couple of years old. I’m trying to find a way to access the elements of the linked RVT. There is the LinkElementId which represents an element in the linked document but I’m struggling to figure out how all this works. Any ideas you can offer would be greatly appreciated.
    Thanks again

  22. Dear Phil,
    This is a an interesting question on a slightly complicated topic that exceeds what I can explain here in a short comment. I suggest that you start by exploring in depth the sample application presented in
    http://thebuildingcoder.typepad.com/blog/2009/02/list-linked-elements.html
    Cheers, Jeremy.

  23. Thanks heaps…I’ll follow it thru.

  24. hi Jeremey
    i am working with revit application.
    i want to covert the revit(.rvt) file to sqlserver file(i:e .mdf extension).
    any helpful way tracked would be really appreciable.

  25. Dear Iss,
    As usual, you should explore an optimal solution for this through the user interface first, before thinking about possibly automating it. For that, I would suggest asking an application engineer, product usage expert, or product support for help on finding an suitable workflow.
    Please let us know what you find out. Thank you.
    Good luck!
    Cheers, Jeremy.

Leave a Reply to BenCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading