Effortless method to duplicate sheets in Revit with views and annotations

, , ,
Duplicate Sheets in Revit

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.

Duplicate Sheets in Revit

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

Your email address will not be published. Required fields are marked *