The linked file enhancements introduced in the Revit 2014 API obviously need more awareness:
CreateLinkReference
was introduced way back in
the Revit 2014 API.
Conversion of geometric references in links is mentioned as one of the major enhancements:
The API calls:
Reference
from a Reference
in an RVT Link.Reference
in an RVT Link from a Reference
in the RVT host file.allow conversion between Reference
objects which reference only the contents of the link and Reference
objects which reference the host.
This allows an application, for example, to look at the geometry in the link, find the needed face, and convert the reference to that face into a reference in the host suitable for use to place a face-based instance.
Also, they allow you to obtain a reference in the host (e.g., from a dimension or family) and convert it to a reference in the link, suitable for use in Element.GetGeometryObjectFromReference
.
This enhancement was often overlooked, and several questions were raised on how to tag an element in a linked file.
Ilia Ivanov used these methods to answer his own question in the Revit API discussion forum thread on tagging linked elements using Revit API:
Question: Hello, Is it possibly to tag a linked element?
And also retrieve the reference of the tagged linked element?
Answer: Hello, I have done it:
RevitLinkInstance link = doc.GetElement( tag.TaggedElementId.LinkInstanceId ) as RevitLinkInstance; Reference refer = new Reference( link.GetLinkDocument() .GetElement( tag.TaggedElementId.LinkedElementId ) ) .CreateLinkReference( link );
For a bit more context, here is a slightly nonsensical sample method to tag all walls in all linked documents, placing all tags in one single constant spot:
/// <summary> /// Tag all walls in all linked documents /// </summary> void TagAllLinkedWalls( Document doc ) { // Point near my wall XYZ xyz = new XYZ( -20, 20, 0 ); // At first need to find our links FilteredElementCollector collector = new FilteredElementCollector( doc ) .OfClass( typeof( RevitLinkInstance ) ); foreach( Element elem in collector ) { // Get linkInstance RevitLinkInstance instance = elem as RevitLinkInstance; // Get linkDocument Document linkDoc = instance.GetLinkDocument(); // Get linkType RevitLinkType type = doc.GetElement( instance.GetTypeId() ) as RevitLinkType; // Check if link is loaded if( RevitLinkType.IsLoaded( doc, type.Id ) ) { // Find walls for tagging FilteredElementCollector walls = new FilteredElementCollector( linkDoc ) .OfCategory( BuiltInCategory.OST_Walls ) .OfClass( typeof( Wall ) ); // Create reference foreach( Wall wall in walls ) { Reference newRef = new Reference( wall ) .CreateLinkReference( instance ); // Create transaction using( Transaction tx = new Transaction( doc ) ) { tx.Start( "Create tags" ); IndependentTag newTag = IndependentTag.Create( doc, doc.ActiveView.Id, newRef, true, TagMode.TM_ADDBY_MATERIAL, TagOrientation.Horizontal, xyz ); // Use TaggedElementId.LinkInstanceId and // TaggedElementId.LinkInstanceId to retrieve // the id of the tagged link and element: LinkElementId linkId = newTag.TaggedElementId; ElementId linkInsancetId = linkId.LinkInstanceId; ElementId linkedElementId = linkId.LinkedElementId; tx.Commit(); } } } } }
Many thanks to Ilia for sharing this!
In another extensive thread
on highlighting and tagging linked elements.
Carolina Machado suggested an alternative approach and less official solution to tag a linked element using the ParseFromStableRepresentation
method instead:
Using RevitLookup and a post from your blog, I noticed that the Stable Representation of references in linked instances conform to the following pattern:
revitLinkInstance.UniqueId
+":0:RVTLINK/" + revitLinkType.UniqueId
+":" + element.Id.ToString()Using this string, it is possible to get the
Reference
throughReference.ParseFromStableRepresentation
method and then use it to tag the element.
Many thanks to Carolina for sharing this!
On a another tagging topic, however with no links involved, here are two suggestions by my colleague Naveen Kumar and Alexander @aignatovich Ignatovich, aka Александр Игнатович, answering a whole slew of questions on how to retrieve all untagged doors in the model:
Question: Can we determine the relationship between a tag and its tagged element?
I can retrieve all independent tags of particular category.
E.g., having 6 doors, I can retrieve the 6 door tags.
Suppose one of doors does not have tag.
How can I find the particular door lacking a tag?
In other words, how to find relation between element category and element tag category.
Answer: Try using the below code. It will highlight the elements that are not tagged:
FilteredElementCollector doors = new FilteredElementCollector( doc ) .OfCategory( BuiltInCategory.OST_Doors ) .OfClass( typeof( FamilyInstance ) ); IEnumerable<IndependentTag> tags = new FilteredElementCollector( doc ) .OfClass( typeof( IndependentTag ) ) .Cast<IndependentTag>(); IList<ElementId> untagged_elements = new List<ElementId>(); foreach( Element e in doors ) { if( !tags.Any( q => q.TaggedLocalElementId == e.Id ) ) { untagged_elements.Add( e.Id ); } } uidoc.Selection.SetElementIds( untagged_elements ); uidoc.RefreshActiveView();
Answer 2: To check whether a specific door is untagged, you can find all IndependentTag
elements present in the document of OST_DoorTags
category and the door elements that they are tagging like this:
var collector = new FilteredElementCollector( doc ) .OfClass( typeof( IndependentTag ) ) .OfCategory( BuiltInCategory.OST_DoorTags ); var doorTagsIds = new HashSet<ElementId>( collector.OfType<IndependentTag>() .Select( x => x.GetTaggedLocalElement()?.Id ) .Where( x => x != null ) );
Then you can iterate over your doors collection and check if doorTagsIds
contains the door.Id
.
Both of these suggestions can probably be speeded up by storing the entire relationship between the tags and the tagged elements in a dictionary and inverting that relationship, as I explained in the recent thread
on FilteredElementCollector
– unreferenced sections only.