A recent discussion on using the ShowElements
method to toggle between documents and views brought up a few interesting points:
Normally, you can open and switch between documents using the OpenAndActivateDocument
method.
However, it requires a file path, which may not have been defined yet, in the case of a newly created document.
In that case, you can open and switch between different documents and their views by calling the UIDocument
ShowElements
method instead.
This was discussed again in the Revit API discussion forum thread on changing the active document, and previously in how to open and active a new document that is not saved:
Question: When you have 2 Revit projects open and want to switch between them, it can be done with:
application.OpenAndActivateDocument(file);
I used Document.PathName
to get the filename required.
Now I run into problems with files on Revit Server and BIM 360 docs, because their Document.Pathname
stays empty.
So, my question is, how do I switch between those documents (when Document.Pathname
is empty)?
Is there a way to switch between documents without having to specify the file name?
Answer: The only way I have found is indirectly via UIDocument.ShowElements
.
You pick an element from the DB document you want to change to, create a UIDocument
object from a DB document and use UIDocument.ShowElements
. You have to handle the occasional “No good view found” dialogue, but it seems to always switch the active document regardless of what is found. Helps if it is a View
specific element.
Not sure if there is a better way...
When you call UIDocument.ShowElements
, the active Document
will change to the document you are showing elements in, therefore:
If you filter for any element in one of the views of the newly created document, you can call it using that element to activate that document.
Sometimes you get the dialogue 'No good view found', which is odd, considering you know there is at least one view with the element in considering you are filtering for it by view. You can handle the appearance of this dialogue and the ActiveDocument
still gets changed. I believe the new document generally has a view with Elevation
markers, hence there is always something to find and show.
I don't know the implications of changing the ActiveDocument
this way or why the API has no ability to directly change the ActiveDocument
directly, but I suspect if it were easy in terms of how the API works, it would have been done by now.
This workaround was also mentioned in The Building Coder discussion on mirroring in a new family and changing active view:
The first issue that arises is that the mirror command requires a current active view, which is not automatically present in the family document. Joe discovers a workaround for that issue using the
ShowElements
method. It generates an unwanted warning message, so a second step is required to deal with eliminating that as well.As you can see on reading the final solution carefully, you can use the
ShowElements
method to change the active view and even switch it between the family and project documents. The official Revit 2011 API does not provide any method to switch the active view, but usingShowElements
can be used to create a workaround for that."
I implemented a new external command CmdSwitchDoc in The Building Coder samples to try this out, in release 2018.0.138.1.
It demonstrates two uses of the ShowElements
method:
/// <summary> /// Zoom to the given elements, switching view if needed. /// </summary> /// <param name="ids"></param> /// <param name="message">Error message on failure</param> /// <param name="elements">Elements causing failure</param> /// <returns></returns> Result ZoomToElements( UIDocument uidoc, ICollection<ElementId> ids, ref string message, ElementSet elements ) { int n = ids.Count; if( 0 == n ) { message = "Please select at least one element to zoom to."; return Result.Cancelled; } try { uidoc.ShowElements( ids ); } catch { Document doc = uidoc.Document; foreach( ElementId id in ids ) { Element e = doc.GetElement( id ); elements.Insert( e ); } message = string.Format( "Cannot zoom to element{0}.", 1 == n ? "" : "s" ); return Result.Failed; } return Result.Succeeded; }
This functionality is similar to that provided by the Zoom to Awesome! add-in by Phil Read.
Here is a 40-second demo by Luke Johnson of using Zoom to Awesome, also showing how to add a keyboard shortcut:
/// <summary> /// Toggle back and forth between two different documents /// </summary> void ToggleViews( View view1, string filepath2 ) { Document doc = view1.Document; UIDocument uidoc = new UIDocument( doc ); Application app = doc.Application; UIApplication uiapp = new UIApplication( app ); // Select some elements in the first document ICollection<ElementId> idsView1 = new FilteredElementCollector( doc, view1.Id ) .WhereElementIsNotElementType() .ToElementIds(); // Open the second file UIDocument uidoc2 = uiapp .OpenAndActivateDocument( filepath2 ); Document doc2 = uidoc2.Document; // Do something in second file using( Transaction tx = new Transaction( doc2 ) ) { tx.Start( "Change Scale" ); doc2.ActiveView.get_Parameter( BuiltInParameter.VIEW_SCALE_PULLDOWN_METRIC ) .Set( 20 ); tx.Commit(); } // Save modified second file SaveAsOptions opt = new SaveAsOptions { OverwriteExistingFile = true }; doc2.SaveAs( filepath2, opt ); // Switch back to original file; // in a new file, doc.PathName is empty if( !string.IsNullOrEmpty( doc.PathName ) ) { uiapp.OpenAndActivateDocument( doc.PathName ); doc2.Close( false ); // no problem here, says Remy } else { // Avoid using OpenAndActivateDocument uidoc.ShowElements( idsView1 ); uidoc.RefreshActiveView(); //doc2.Close( false ); // Remy says: Revit throws the exception and doesn't close the file } }