Friday, February 20, 2015

Quick Macro for Duplicating an Existing Sheet and Views

I had someone recently ask me if it was possible to duplicate a sheet and it's views in Revit. It isn't possible within Revit by default, but with a little bit of code, I was able to get a macro that would duplicate the sheet and it's views. Give the macro a try, just make sure you are in a sheet view before running the macro.

Note: the duplicated views and sheets are renamed with "-DUP" since they need to be unique in Revit. You can change this in the code to be anything you want.

public void DuplicateSheet()
{
    UIDocument uidoc = this.ActiveUIDocument;
    Document doc = uidoc.Document;
    ViewSheet vs = doc.ActiveView as ViewSheet;
   
    using(Transaction t = new Transaction(doc, "Duplicate Sheet"))
    {
        t.Start();
   
        FamilyInstance titleblock = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance))
            .OfCategory(BuiltInCategory.OST_TitleBlocks).Cast<FamilyInstance>()
            .First(q => q.OwnerViewId == vs.Id);

        ViewSheet newsheet = ViewSheet.Create(doc, titleblock.GetTypeId());
        newsheet.SheetNumber = vs.SheetNumber + "-DUP";
        newsheet.Name = vs.Name;
       
        // all views but schedules
        foreach(ElementId eid in vs.GetAllPlacedViews())
        {
            View ev = doc.GetElement(eid) as View;
           
            View newview = null;
           
            // legends
            if (ev.ViewType == ViewType.Legend)
            {
                newview = ev;
            }
            // all non-legend and non-schedule views
            else
            {
                ElementId newviewid = ev.Duplicate(ViewDuplicateOption.WithDetailing);
                newview = doc.GetElement(newviewid) as View;
                newview.Name = ev.Name + "-DUP";
            }
           
            foreach (Viewport vp in new FilteredElementCollector(doc).OfClass(typeof(Viewport)))
            {
                                               
                if (vp.SheetId == vs.Id && vp.ViewId == ev.Id)
                {
                    BoundingBoxXYZ vpbb = vp.get_BoundingBox(vs);
                    XYZ initialCenter = (vpbb.Max + vpbb.Min) / 2;
                   
                    Viewport newvp = Viewport.Create(doc, newsheet.Id, newview.Id, XYZ.Zero);
                   
                    BoundingBoxXYZ newvpbb = newvp.get_BoundingBox(newsheet);
                    XYZ newCenter = (newvpbb.Max + newvpbb.Min) / 2;
                   
                    ElementTransformUtils.MoveElement(doc, newvp.Id, new XYZ(
                        initialCenter.X - newCenter.X,
                        initialCenter.Y - newCenter.Y,
                        0));
                }
                   
            }
           
        }
       
        // schedules
       
        foreach (ScheduleSheetInstance si in (new FilteredElementCollector(doc).OfClass(typeof(ScheduleSheetInstance))))
        {
            if (si.OwnerViewId == vs.Id)
            {
                if (!si.IsTitleblockRevisionSchedule)
                {                           
                    foreach (ViewSchedule vsc in new FilteredElementCollector(doc).OfClass(typeof(ViewSchedule)))
                    {
                        if (si.ScheduleId == vsc.Id)
                        {
                            BoundingBoxXYZ sibb = si.get_BoundingBox(vs);
                            XYZ initialCenter = (sibb.Max + sibb.Min) / 2;
                           
                            ScheduleSheetInstance newssi = ScheduleSheetInstance.Create(doc, newsheet.Id, vsc.Id, XYZ.Zero);
                           
                            BoundingBoxXYZ newsibb = newssi.get_BoundingBox(newsheet);
                            XYZ newCenter = (newsibb.Max + newsibb.Min) / 2;
                           
                            ElementTransformUtils.MoveElement(doc, newssi.Id, new XYZ(
                                initialCenter.X - newCenter.X,
                                initialCenter.Y - newCenter.Y,
                                0));
                        }
                    }
                }

            }
        }
       
   
        t.Commit();
    }
   
}

31 comments:

  1. Hi Troy, Thanks for this macro, I shared your link with my co-workers that use Revit and we find all your macros super useful and helpful to us.
    I attended the SCRUG meeting on Friday in Irvine and I am fascinated with Macros. I currently use AutoCAD 2007 and have started to explore Revit on my free time. This was my first SCRUG meeting and really enjoyed your presentation. Someone at our office has created an extensive list of macros that we use on a daily basis. I don't know how we ever did anything without them, that's how much we rely on them. Before your presentation on Friday, I knew nothing about macros as long as they worked when I typed the command that was all I was concerned about. Now I want to create a macro for everything. I've been going around all week saying "I'm going to write a macro for that" to everybody at work. So now, where do I start? Do you have any recommendations on Intro books I can purchase or on-line courses I could enroll? I realize I can just do a search and find out but I wanted to ask you because the way you presented the information at the meetup made absolute sense to me. I was able to follow everything you were saying was never like I had been dealing with macros for years. This may sound dramatic but it was like a blindfold had been taken off and all the sudden the possibilities are endless. The language used, C#, is very logical, I've been living in a macro fantasyland, so to speak. But now, in real time I need to be proactive and actually start working with macros, especially since I went around telling everybody that I was going to create a whole library of them specifically for Revit. My first task I want take on (the person that created these macros has not been able to figure out) is to take all the macros that were created using VBA and update them to be compatible with the current Revit API after that I am going to create a macro for steel beams connected to columns, snap to and connect to top of column. Any suggestions on some books or which links from your presentation handout would be the best fit to help me accomplish these tasks?

    I very much appreciate all your input, knowledge and expertise you share.

    -Marlyn
    (Future macro developer)

    ReplyDelete
  2. It's another hit Troy! Thanks so much.

    ReplyDelete
  3. Hi, I am very interested in running this macro and am new into this. Initially I was having another error because the code was expecting two } at the end. I fixed that and now got this new error which I don't know how to fix:

    'Autodesk.Revit.DB.ViewSheet' does not contain a definition for 'GetAllPlacedViews' and no extension method 'GetAllPlacedViews' accepting a first argument of type 'Autodesk.Revit.DB.ViewSheet' could be found (are you missing a using directive or an assembly reference?) (CS1061) - C:\ProgramData\Autodesk\Revit\Macros\2014\Revit\AppHookup\DuplicateSheets\Source\DuplicateSheets\ThisApplication.cs:58,37

    Any ideas about it? Thank you for sharing this!

    ReplyDelete
  4. It works perfectly in Revit 2015, but in 2014 it was complaining about the GetAllPlacedViews.
    Great work! Thanks again...

    ReplyDelete
  5. Francisco, the GetAllPlacedViews is a renamed method in Revit 2015. In 2014 the method is called GetAllViewports. You should be able to edit it for 2014 using the different name for the method.

    ReplyDelete
  6. Hi Troy,
    I'm not very familiar with Revit Macros and I've been trying to run yours in Revit 2015 as it would be extremely useful, but I'm getting the following error:
    'DuplicateSheet.ThisDocument' does not contain a definition for 'ActiveUIDocument' and no extension method 'ActiveUIDocument' accepting a first argument of type 'DuplicateSheet.ThisDocument' could be found (are you missing a using directive or an assembly reference?) (CS1061) - C:\Users\pablozamorano\AppData\Local\Temp\{AB9FA4D7-A098-4131-94F8-EB86FBD1DCEF}\Revit\DocHookups2592\1092319040\DuplicateSheet\Source\DuplicateSheet\ThisDocument.cs:23,29
    Do you know any way of solving this?
    Thank you!
    Pablo.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi Troy,
    I'm not very familiar with Revit Macros and I've been trying to run yours in Revit 2015 as it would be extremely useful, but I'm getting the following error:
    'DuplicateSheet.ThisDocument' does not contain a definition for 'ActiveUIDocument' and no extension method 'ActiveUIDocument' accepting a first argument of type 'DuplicateSheet.ThisDocument' could be found (are you missing a using directive or an assembly reference?) (CS1061)
    Do you know any way of solving this?
    Thank you!
    Pablo.

    ReplyDelete
  9. PEZ, when copying this macro into Revit, you need to make sure you are doing it in the Application tab and not the Project file tab.

    ReplyDelete
  10. Thanks for your reply Troy, It worked perfect!
    Excellent work!

    ReplyDelete
  11. Hello Troy,

    Genius script this. Well impressed. As with some of the above comments, I'm using Revit 2014 for an ongoing large scale project. I'm new to Macros but believe I'm pretty close with this (Having watched a couple of basic Revit Macro youtube tutorials). I've got an error message as follows:

    Revit failed to execute DuplicateSheets.

    A problem has been detected.

    System.NullReferenceException: Object reference not set to an instance of an object.
    at MacroModuel.executeMacro_(MacroModule*, AString*macroName)
    at MacroModule.executeMacro(MacroModule*, AString* )
    at
    UIMacroGeneralManager.runMacro(UIMacroGeneralManager*, MacroModule*pModule, AString* macroName)

    Any light you could shed on this would be much appreciated.

    Thanks!

    ReplyDelete
  12. This tool came in handy a number of times
    Is it possible to retrieve additional shared parameters from a sheet as well as title on sheet within this routine? Would get/set parameters work with this? We have a few QC parameters that I want to carry over.

    ReplyDelete
  13. William, yes you can definitely get parameter info and then set the same data to the new sheet. Something like the following:

    string paramaterData = vs.LookupParameter("Paramater name"));
    newsheet.LookupParameter("Parameter name").Set(parameterData);

    Depending on the parameter you will need to use the appropriate data type whether its a string, integer, double, etc.

    ReplyDelete
  14. I have tried a couple different variations of what you have showed but I keep getting this error "Cannot convert method group 'ToString' to non-delegate type 'string'

    This was the last iteration I tried
    Parameter paramDisc = vs.LookupParameter("SHEET DISCIPLINE");
    Parameter paramDisc2 = newsheet.LookupParameter("SHEET DISCIPLINE");
    string disc = paramDisc2.ToString;
    paramDisc2.Set(disc);

    The parameter is a text parameter so I am not sure why this is not working.

    ReplyDelete
    Replies
    1. I believe for text parameters you need to use AsString() instead of ToString(). So in your code it would be...

      string disc = paramDisc2.AsString();

      Delete
  15. Thank you, it works as expected! Great!

    ReplyDelete
  16. Would this work in Revit 2013? What steps/format in setup are required to get this working?

    ReplyDelete
  17. Will this work for Revit 2013?

    How would I go about setting this up from a project that doesn't have macros enabled yet?

    ReplyDelete
    Replies
    1. Sorry the code for this only works in 2015 and later because Autodesk added the GetAllPlacedViews method to sheets. I'm sure this code could be altered for 2013, but it would require some code to figure out the views on a sheet.

      Delete
  18. Great Macro Troy, Thank you. Question, how can I modify this macro to also copy text from sheet to sheet?

    ReplyDelete
  19. Hi Troy,

    I discovered the macro Revit. For a project to version 2014, I need to duplicate the sheets and views ... Your macro is awesome. I get to run version 2015, but not version 2014. I have a bug - see picture (https://drive.google.com/open?id=0B3f36KC7VY_fNW5pVEtJdlM5M3M)
    How to fix the problem
    Thank you very much for your help !

    Franck

    ReplyDelete
    Replies
    1. Sorry the code for this only works in 2015 and later because Autodesk added the GetAllPlacedViews method to sheets. I'm sure this code could be altered for 2014, but it would require some code to figure out the views on a sheet.

      Delete
  20. Hi Troy,

    First off this is a real slick Macro and I can see some real time savings for setting up projects. I am running it in Revit 2016 and am noticing that it isn't placing the views in the same place from sheet to sheet. They are slightly unaligned every time I duplicate. Any ideas on how to get it to duplicate to the same location on every sheet? Seems like the floorplan, legends, and legends all kinda drift up and to the right. Just curious if anyone else is having the same issue and if there is anything I can try to get them to line up. Thanks again for the Macro! Awesome work!

    ReplyDelete
  21. I am trying to get this to work in Revit 2016 and it will not complete the build. This is the error I get:
    'DuplicateSheet.ThisDocument' does not contain a definition for 'ActiveUIDocument' and no extension method 'ActiveUIDocument' accepting a first argument of type 'DuplicateSheet.ThisDocument' could be found (are you missing a using directive or an assembly reference?) (CS1061) - D:\TEMP Files\{0AE32A0F-73D8-436A-A055-362F53B9FD0F}\Revit\DocHookups8980\1448778112\DuplicateSheet\Source\DuplicateSheet\ThisDocument.cs:43,29

    any ideas?

    ReplyDelete
  22. I am trying to use this in Revit 2016 but will not complete the build. this is the error I am getting:

    'DuplicateSheet.ThisDocument' does not contain a definition for 'ActiveUIDocument' and no extension method 'ActiveUIDocument' accepting a first argument of type 'DuplicateSheet.ThisDocument' could be found (are you missing a using directive or an assembly reference?) (CS1061) - D:\TEMP Files\{0AE32A0F-73D8-436A-A055-362F53B9FD0F}\Revit\DocHookups8980\1448778112\DuplicateSheet\Source\DuplicateSheet\ThisDocument.cs:43,29

    Any ideas?

    ReplyDelete
    Replies
    1. My guess is that you are trying to put this into a Project macro instead of an Application macro.

      Delete
    2. That was it. Is there a way to make this Macro a "Project" macro instead of an "Application' macro?

      Delete
    3. Change: UIDocument uidoc = this.ActiveUIDocument;

      to: UIDocument uidoc = Application.ActiveUIDocument;

      Delete
  23. Thank you!!! You are awesome!!

    ReplyDelete
  24. Is there a way to define which "viewport" used when the sheet is duplicated? As of now, it just goes to the default viewport type. Thanks again for your expertise in this area.

    ReplyDelete
    Replies
    1. Unfortunately no. I had the same issue and looked into the API and there is nothing in the API to pick the type of viewport.

      Delete