In response to my post “Transferring just one View Template from Project to Project“, Mike asks:
How would you implement this to copy sheets?
To start, it is not possible to copy viewports with the API. This is not too much of a surprise, as the Revit UI does not allow duplicating sheets and does not allow copying viewports. Also, it is not possible to copy a sheet that has viewports on it.
However, it is possible to use the API to copy the sheet, titleblock, and view-specific elements on the sheet if we use this procedure:
- Temporarily delete the viewports from the sheet (this is done in a transaction that be rolled back at the end so that the viewports are not actually deleted)
- Copy the sheet to the new project
- Copy the view-specific elements (excluding the viewports) from the source sheet view to the destination sheet view
The sneaky part of this is realizing that that while a single document can have only one transaction open at any given time, we can have one transaction in each document open at the same time.
public void CopySheet()
{
Document doc = this.ActiveUIDocument.Document;
ViewSheet activeViewSheet = doc.ActiveView as ViewSheet;
if (activeViewSheet == null)
{
TaskDialog.Show("Error", "Active view must be a sheet.");
return;
}
Application app = this.Application;
Document otherDoc = app.Documents.Cast<Document>().Where(d => d.Title != doc.Title).FirstOrDefault();
if (otherDoc == null)
{
TaskDialog.Show("Error", "There must be a 2nd document open.");
return;
}
// put the sheet in the source document into the copyIds collection
ICollection<ElementId> copyIds = new Collection<ElementId>();
copyIds.Add(activeViewSheet.Id);
// put view-specific elements on the sheet in the copyIdsViewSpecific collection
ICollection<ElementId> copyIdsViewSpecific = new Collection<ElementId>();
foreach (Element e in new FilteredElementCollector(doc).OwnedByView(doc.ActiveView.Id))
{
// do not put viewports into this collection because they cannot be copied
if (!(e is Viewport))
copyIdsViewSpecific.Add(e.Id);
}
// Create a transaction in the source document to delete the viewports.
// This transaction will be rolled-back so it won't cause any permanent change in the document
// but it will enable copying of the sheet while it is in a state with no viewports
using (Transaction t = new Transaction(doc,"Delete Viewports"))
{
t.Start();
IList<Viewport> viewports = new FilteredElementCollector(doc).OfClass(typeof(Viewport)).Cast<Viewport>()
.Where(q => q.SheetId == activeViewSheet.Id).ToList();
foreach (Viewport vp in viewports)
{
doc.Delete(vp.Id);
}
using (Transaction tOther = new Transaction(otherDoc, "Copy View Template"))
{
tOther.Start();
// copy the sheet using the CopyElements overload that accepts source and destination documents
// get the newly created sheet in the target document - it will be the first (and only) element returned by ElementTransformUtils.CopyElements
ViewSheet newSheet = otherDoc.GetElement(ElementTransformUtils.CopyElements(doc, copyIds, otherDoc, Transform.Identity, new CopyPasteOptions()).First()) as ViewSheet;
// copy the view-specific elements using the CopyElements overload that accepts source and destination views
ElementTransformUtils.CopyElements(activeViewSheet, copyIdsViewSpecific, newSheet, Transform.Identity, new CopyPasteOptions());
tOther.Commit();
}
// rollback the transaction to "undo" the deletion of the viewports
t.RollBack();
}
}