List Linked Elements

We already had a look at the issue of

linked files

and

how to hide them
.
A frequent question in this context is how to access the elements in linked files.
This can be very simple, actually, as we will demonstrate.
I had a discussion on this with Joel Karr of

Environmental Systems Design, Inc
.
He is implementing a command to order to monitor and compare the lighting fixtures in an MEP model with the ones defined in a linked in architectural model.
He very kindly provided a sample application listing the lighting fixture elements contained in a linked file, as a starting point for implementing a comparison of those with the ones in the current model.
I rewrote and significantly simplified his application and find that we can achieve a lot of functionality with minimal effort.

For this purpose we implemented a new external command CmdLinkedFileElements which iterates over all open documents, which includes the linked ones as well, and displays selected properties for all lighting fixture elements in a data grid.

Displaying element properties

We define a class ElementData to manage the properties we wish to display:

  • Document, the project containing the element.
  • Element, the element name.
  • Id, the element id.
  • X, Y and Z, the family instance location point coordinates.
  • UniqueId, the unique id.
  • Folder, the document folder.

These data items are stored in individual private members.
We define a constructor in order to initialise all the members for a given Revit element.
By defining public properties for accessing each data item to display, we can save the effort of transferring data manually into the form.
Here is the class implementation including member data, constructor, and accessors:


public class ElementData
{
  string _document;
  string _elementName;
  int _id;
  double _x;
  double _y;
  double _z;
  string _uniqueId;
  string _folder;
 
  public ElementData(
    string path,
    string elementName,
    int id,
    double x,
    double y,
    double z,
    string uniqueId )
  {
    int i = path.LastIndexOf( "\" );
    _document = path.Substring( i + 1 );
    _elementName = elementName;
    _id = id;
    _x = x;
    _y = y;
    _z = z;
    _uniqueId = uniqueId;
    _folder = path.Substring( 0, i );
  }
 
  public string Document {
    get { return _document; }
  }
  public string Element {
    get { return _elementName; }
  }
  public int Id {
    get { return _id; }
  }
  public string X {
    get { return Util.RealString( _x ); }
  }
  public string Y {
    get { return Util.RealString( _y ); }
  }
  public string Z {
    get { return Util.RealString( _z ); }
  }
  public string UniqueId {
    get { return _uniqueId; }
  }
  public string Folder {
    get { return _folder; }
  }
}

With these properties defined, displaying the data in the form is handled completely automatically by one single line in the form constructor:


public CmdLinkedFileElementsForm(
  List<ElementData> a )
{
  InitializeComponent();
  dataGridView1.DataSource = a;
}

Collecting element data from linked files

We have discussed how to display the element data in a data grid.
Before we can display it, we need to retrieve it from the Revit database.
To do so, we can simply iterate over all the open documents in the application.
If desired, we can implement a filter to eliminate the documents that do not represent a linked file.

We implement a utility method to filter for elements matching a given category and type.


public List<Element> GetElements(
  BuiltInCategory bic,
  Type elemType,
  Application app,
  Document doc )
{
  CreationFilter cf = app.Create.Filter;
  Filter f1 = cf.NewCategoryFilter( bic );
  Filter f2 = cf.NewTypeFilter( elemType );
  Filter f3 = cf.NewLogicAndFilter( f1, f2 );
  List<Element> elements = new List<Element>();
  doc.get_Elements( f3, elements );
  return elements;
}

This can be used both to extract the lighting fixtures to display in the form as well as the linked files instances, if we need those.
We will only use it for the lighting fixtures in the real code.
For completeness sake, this would be the call to retrieve the link instances:


  List<Element> links = GetElements(
    BuiltInCategory.OST_RvtLinks,
    typeof( Instance ), app, doc );

The external command performs the following steps:

  • Iterate over the application documents.
  • Select all lighting fixtures in each linked document.
  • Instantiate and populate an ElementData instance for each fixture.
  • Display the form with the data collected.

Here is the mainline for the command:


List<ElementData> data = new List<ElementData>();
Application app = commandData.Application;
DocumentSet docs = app.Documents;
foreach( Document doc in docs )
{
  List<Element> elements = GetElements(
    BuiltInCategory.OST_LightingFixtures,
    typeof( FamilyInstance ), app, doc );
 
  foreach( FamilyInstance e in elements )
  {
    string name = e.Name;
    LocationPoint lp = e.Location as LocationPoint;
    if( null != lp )
    {
      XYZ p = lp.Point;
      data.Add( new ElementData( doc.PathName, e.Name,
        e.Id.Value, p.X, p.Y, p.Z, e.UniqueId ) );
    }
  }
}
using( CmdLinkedFileElementsForm dlg = new CmdLinkedFileElementsForm( data ) )
{
  dlg.ShowDialog();
}
return CmdResult.Cancelled;

Notice how short and sweet this is?

Here is an example of the command displaying the data in the sample model provided by Joel:

Linked file element data

This can obviously easily be adapted to handle other element types or more general selections than just lighting fixtures.
For the moment, of course, we are ignoring all the thorny issues to do with transformations and stuff.
I am looking forward to your comments on this one.

Here is
version 1.0.0.27
of the complete Visual Studio solution with the new CmdLinkedFileElements command.
It also includes the new command CmdNewRailing which unfortunately does not create a new railing instance.
The reasons for this can be found in the

discussion with Berria
.


Comments

7 responses to “List Linked Elements”

  1. Prashant Avatar
    Prashant

    Hi Jeremy,
    I have a filter to get all the elements of type FamilySymbol, and then I try to get the Name and Category.Name for each element obtained.
    It works fine if the document is the primary document (ActiveDocument), but if I have a linked document, any (read)action related to Category throws the following exception:
    “Attempt to modify the model outside of transaction”.
    This exception is not thrown for element.Name only for element.Category.Name
    In fact the same exception is thrown if I just do:
    “If NOT (element.Category is Nothing) then
    End If”
    Any help is greatly appreciated !

  2. Prashant Avatar
    Prashant

    Jeremy,
    I found the answer at AUGI Revit API forum, Matt pointed it out. Here’s the link:
    http://forums.augi.com/showthread.php?t=104316
    Regards

  3. Dear Prashant,
    Thank you for the interesting question, and even more for the pointer to the valuable answer from Matt Mason pointing out the importance of wrapping all your interactions with Revit documents in a transaction. For access to the currently active document, Revit automatically opens and manages a transaction for you, but not for any other ones.
    Cheers, Jeremy.

  4. Hello Jeremy.
    I need to change parameter of linked elements via API. I had tried to do this using code:
    var app = commandData.Application.Application;
    var doc = commandData.Application.ActiveUIDocument.Document;
    //get linked documents
    var linkedDocs = GetLinkedDocuments(doc).ToList();
    if (linkedDocs.Count == 0) return Result.Succeeded;
    var firstLinkedDoc = linkedDocs[0];
    FilteredElementCollector collector = new FilteredElementCollector(firstLinkedDoc);
    var walls = collector
    .OfCategory(BuiltInCategory.OST_Walls)
    .OfType()
    .ToList();
    foreach (var wall in walls)
    {
    var urlParam = wall.get_Parameter(BuiltInParameter.ALL_MODEL_URL);
    if (urlParam != null)
    urlParam.Set(firstLinkedDoc.PathName);
    }
    }
    But when I try to set parameter exception is throw: “Attempt to modify the model outside of transaction.”
    If I create new Transaction in linked document other exception is throw: “Document is a linked file. Transactions can only be used in primary documents (projects or families.)”
    So the solution that describes in AUGI Revit API forum http://forums.augi.com/showthread.php?t=104316 does not work.
    I use Revit Architecture 2012.
    Jeremy, is it possible to change parameter in linked elements?
    Thanks for you great blog!

  5. Hi Victor,
    Thank you for your appreciation!
    The AUGI post refers to a previous version of Revit.
    For Revit 2012, please refer to the remarks on the Transaction class in the Revit API Help file RevitAPI.chm. They state:
    “Transactions in linked documents are not permitted, for linked documents are not allowed to be modified.”
    Cheers, Jeremy.

  6. Hi Jeremy,
    Is it possible to open each link and change elements’ properties and close upon successful changes?
    Thanks

  7. Hello Jeremy,
    How can select a object in a link document when i have the ID.
    We can zoom on the object, but I can not select it.
    What is the problem in my code?
    My code:
    List ^oDocumentLierListe;
    ICollection ^oCollectionSelectionLier;
    Transaction ^oTrans;
    Element ^oElement;
    ElementId ^oElemId;
    UIDocument ^oUIDocumentLier;
    oDocumentLierListe = gcnew List();
    oTrans = gcnew Transaction(this->oDocument, “Read FISA”);
    oTrans->Start();
    ::GetDocumentLier(oDocumentLierListe, this->oDocument, this->oUIApplication);
    oElemId = gcnew ElementId(1756242);
    for each(Document ^oDocLier in oDocumentLierListe)
    {
    oElement = oDocLier->GetElement(oElemId);
    if (oElement != nullptr)
    {
    oUIDocumentLier = gcnew UIDocument(oDocLier);
    oCollectionSelectionLier = oUIDocumentLier->Selection->GetElementIds();
    oCollectionSelectionLier->Clear();
    oCollectionSelectionLier->Add(oElement->Id);
    oUIDocumentLier->Selection->SetElementIds(oCollectionSelectionLier);
    oUIDocumentLier->ShowElements(oElement);
    }
    }
    oTrans->Commit();

Leave a Reply to PrashantCancel reply

Discover more from Autodesk Developer Blog

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

Continue reading