A common problem in Revit when drafting is the necessity to duplicate a sheet with views. This is done to recreate the sheet with some slight variation, for example, with different annotations or to a swap view. In this post, we are going to address this issue, as Revit does not allow duplicate sheets with views out of the box.
Overview of duplicate sheets in Revit
Following the post A comprehensive guide to creating a customized Revit tab in C#, we are going to keep building on top of that ribbon tab. In line with the specifications defined in that post, we are going to add the folders, file, and code necessary in a new directory.
Create a button in the tab to duplicate sheets
1. Add a folder named DuplicateSheets into the project.
2. Inside this folder, add another folder called Images and place the icons inside.
3. Create a new class, name it DuplicateSheets.cs and add the code below:
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Collections.Generic;
using System.Linq;
namespace BIMiconToolbar.DuplicateSheets
{
[TransactionAttribute(TransactionMode.Manual)]
class DuplicateSheets : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
Document doc = commandData.Application.ActiveUIDocument.Document;
// Check the current active view
View selView = doc.ActiveView;
ViewSheet vSheet = doc.ActiveView as ViewSheet;
// Retrieve titleblock from current sheet and all elements in view
var titleblock = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_TitleBlocks).Cast<FamilyInstance>()
.First(q => q.OwnerViewId == vSheet.Id);
var elementsInViewId = new FilteredElementCollector(doc, selView.Id).ToElementIds();
// Retrieve viewports in view
FilteredElementCollector viewPorts = new FilteredElementCollector(doc, selView.Id).OfClass(typeof(Viewport));
// Retrieve schedules in view
FilteredElementCollector schedules = new FilteredElementCollector(doc).OwnedByView(selView.Id)
.OfClass(typeof(ScheduleSheetInstance));
// Retrieve viewSchedules
FilteredElementCollector viewSchedules = new FilteredElementCollector(doc).OfClass(typeof(ViewSchedule));
// Store copied elements and annotation elements
var copiedElementIds = new List<ElementId>();
var annotationElementsId = new List<ElementId>();
using (Transaction t = new Transaction(doc, "Duplicate Sheet"))
{
// Start transaction to duplicate sheet
t.Start();
// Duplicate sheet
ViewSheet newsheet = ViewSheet.Create(doc, titleblock.GetTypeId());
newsheet.SheetNumber = vSheet.SheetNumber + "-DUP";
newsheet.Name = vSheet.Name;
// Get origin of the titleblock
XYZ originTitle = titleblock.GetTransform().Origin;
// Check titleblock position
Element copyTitleBlock = new FilteredElementCollector(doc).OwnedByView(newsheet.Id).OfCategory(BuiltInCategory.OST_TitleBlocks).FirstElement();
LocationPoint titleLoc = copyTitleBlock.Location as LocationPoint;
XYZ titleLocPoint = titleLoc.Point;
// Check if title block is in the same position as original
if (titleLocPoint.DistanceTo(originTitle) != 0)
{
// Move it in case it is not
titleLoc.Move(originTitle);
}
// Retrieve all views placed on sheet except schedules
foreach (ElementId eId in vSheet.GetAllPlacedViews())
{
View origView = doc.GetElement(eId) as View;
View newView = null;
// Legends
if (origView.ViewType == ViewType.Legend)
{
newView = origView;
}
// Rest of view types
else
{
if (origView.CanViewBeDuplicated(ViewDuplicateOption.WithDetailing))
{
ElementId newViewId = origView.Duplicate(ViewDuplicateOption.WithDetailing);
newView = doc.GetElement(newViewId) as View;
newView.Name = origView.Name + "-DUP";
}
}
// Loop through viewports
foreach (Viewport vp in viewPorts)
{
if (vp.SheetId == vSheet.Id && vp.ViewId == origView.Id)
{
// Retrieve centerpoint of original viewport
XYZ center = vp.GetBoxCenter();
// Create viewport in the original spot
Viewport newVp = Viewport.Create(doc, newsheet.Id, newView.Id, center);
}
// Add element in copied list
copiedElementIds.Add(vp.Id);
}
// Add element in copied list
copiedElementIds.Add(eId);
}
// Retrieve and copy schedules
foreach (ScheduleSheetInstance sch in schedules)
{
// Check schedule is not a revision inside titleblock
if (!sch.IsTitleblockRevisionSchedule)
{
foreach (ViewSchedule vsc in viewSchedules)
{
if (sch.ScheduleId == vsc.Id)
{
// Retrieve center of schedule
XYZ schCenter = sch.Point;
// Create schedule in the same position
ScheduleSheetInstance newSch = ScheduleSheetInstance.Create(doc, newsheet.Id, vsc.Id, schCenter);
}
copiedElementIds.Add(vsc.Id);
}
}
}
// Duplicate annotation elements
foreach (ElementId eId in elementsInViewId)
{
if (!copiedElementIds.Contains(eId))
{
annotationElementsId.Add(eId);
}
}
// Copy annotation elements
ElementTransformUtils.CopyElements( selView, annotationElementsId, newsheet, null, null );
// Commit transaction
t.Commit();
return Result.Succeeded;
}
}
}
}
4. In the BIMiconUI class, add the following code:
Autodesk.Revit.UI.RibbonPanel panelSheets = application.CreateRibbonPanel(tabName, "Sheets");
// Retrieve assembly path
string assemblyPath = Assembly.GetExecutingAssembly().Location;
/*---Ribbon Panel Project---*/
#region Ribbon Panel Project
//Create push buttons for panelProject
PushButtonData buttonNumberDoors = new PushButtonData(
"NumberDoors",
"Number\nDoors",
assemblyPath,
"BIMiconToolbar.NumberDoors.NumberDoors2020"
);
PushButton pbNumberDoors = panelProject.AddItem(buttonNumberDoors) as PushButton;
pbNumberDoors.LargeImage = new BitmapImage(new Uri("pack://application:,,,/BIMiconToolbar;component/NumberDoors/Images/iconNumberDoors.png"));
pbNumberDoors.Image = new BitmapImage(new Uri("pack://application:,,,/BIMiconToolbar;component/NumberDoors/Images/iconNumberDoorsSmall.png"));
RibbonToolTip numberDoorsToolTip = Auxiliar.ButtonToolTip("NumberDoorsHelp.mp4",
"BIMiconToolbar.NumberDoors.Images.NumberDoorsHelp.mp4",
"Number doors according to room number.",
"Assigns a door number according to room. The primary parameter to use for door number" +
"is picked from the ToRoom paramter from the door. If there is no room on either side" +
"of the door, no number will be assigned.");
Auxiliar.SetRibbonItemToolTip(pbNumberDoors, numberDoorsToolTip);
#endregion
/*---Ribbon Panel Sheets---*/
#region Ribbon Panel Sheets
// Duplicate sheets
PushButtonData buttonDuplicateSheets = new PushButtonData(
"DuplicateSheets",
"Duplicate\nSheets",
assemblyPath,
"BIMiconToolbar.DuplicateSheets.DuplicateSheets"
);
PushButton pbDuplicateSheets = panelSheets.AddItem(buttonDuplicateSheets) as PushButton;
pbDuplicateSheets.LargeImage = new BitmapImage(new Uri("pack://application:,,,/BIMiconToolbar;component/DuplicateSheets/Images/iconDupSheets.png"));
pbDuplicateSheets.Image = new BitmapImage(new Uri("pack://application:,,,/BIMiconToolbar;component/DuplicateSheets/Images/iconDupSheetsSmall.png"));
pbDuplicateSheets.ToolTip = "Duplicate active sheet.";
pbDuplicateSheets.LongDescription = "Duplicate current active sheet with detailing and annotation elements.";
5. Build it and launch Revit, now it is ready to duplicate active sheets with views and annotations!
A few words about the code. The views are classified and replicated according to their methods. The annotation elements on the sheet, like revision clouds, dimensions, text, etc are copied with this method, ElementTransformUtils. This will allow duplicate sheets without replicating annotations.
Download the latest compiled library here.
Leave a Reply