// // (C) Copyright 2003-2019 by Autodesk, Inc. All rights reserved. // // Permission to use, copy, modify, and distribute this software in // object code form for any purpose and without fee is hereby granted // provided that the above copyright notice appears in all copies and // that both that copyright notice and the limited warranty and // restricted rights notice below appear in all supporting // documentation. // // AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL ITS FAULTS. // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // // Use, duplication, or disclosure by the U.S. Government is subject to // restrictions set forth in FAR 52.227-19 (Commercial Computer // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. using System; using System.Collections.Generic; using System.Linq; using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.UI; namespace Revit.SDK.Samples.DuplicateViews.CS { /// /// Utilities that facilitate duplication of drafting views and schedules into another document. /// class DuplicateViewUtils { /// /// Utility to duplicate schedules from one document to another. /// /// The source document. /// The collection of schedule views. /// The target document. public static void DuplicateSchedules(Document fromDocument, IEnumerable views, Document toDocument) { // Use LINQ to convert to list of ElementIds for use in CopyElements() method List ids = views.AsEnumerable().ToList().ConvertAll(ViewConvertToElementId); // Duplicate. Pass false to make the function skip returning the map from source element to its copy DuplicateElementsAcrossDocuments(fromDocument, ids, toDocument, false); } /// /// Utility to duplicate drafting views and their contents from one document to another. /// /// The source document. /// The collection of drafting views. /// The target document. /// The number of drafting elements created in the copied views. public static int DuplicateDraftingViews(Document fromDocument, IEnumerable views, Document toDocument) { // Return value int numberOfDetailElements = 0; // Transaction group for all activities using (TransactionGroup tg = new TransactionGroup(toDocument, "API - Duplication across documents with detailing")) { tg.Start(); // Use LINQ to convert to list of ElementIds for use in CopyElements() method List ids = views.AsEnumerable().ToList().ConvertAll(ViewConvertToElementId); // Duplicate. Pass true to get a map from source element to its copy Dictionary viewMap = DuplicateElementsAcrossDocuments(fromDocument, ids, toDocument, true); // For each copied view, copy the contents foreach (ElementId viewId in viewMap.Keys) { View fromView = fromDocument.GetElement(viewId) as View; View toView = toDocument.GetElement(viewMap[viewId]) as View; numberOfDetailElements += DuplicateDetailingAcrossViews(fromView, toView); } tg.Assimilate(); } return numberOfDetailElements; } /// /// Duplicates a set of elements across documents. /// /// The source document. /// Collection of view ids. /// The target document. /// True to return a map of matching elements /// (matched by Name). False to skip creation of this map. /// The map of matching elements, if findMatchingElements was true. private static Dictionary DuplicateElementsAcrossDocuments(Document fromDocument, ICollection elementIds, Document toDocument, bool findMatchingElements) { // Return value Dictionary elementMap = new Dictionary(); ICollection copiedIds; using (Transaction t1 = new Transaction(toDocument, "Duplicate elements")) { t1.Start(); // Set options for copy-paste to hide the duplicate types dialog CopyPasteOptions options = new CopyPasteOptions(); options.SetDuplicateTypeNamesHandler(new HideAndAcceptDuplicateTypeNamesHandler()); // Copy the input elements. copiedIds = ElementTransformUtils.CopyElements(fromDocument, elementIds, toDocument, Transform.Identity, options); // Set failure handler to hide duplicate types warnings which may be posted. FailureHandlingOptions failureOptions = t1.GetFailureHandlingOptions(); failureOptions.SetFailuresPreprocessor(new HidePasteDuplicateTypesPreprocessor()); t1.Commit(failureOptions); } // Find matching elements if required if (findMatchingElements) { // Build a map from name -> source element Dictionary nameToFromElementsMap = new Dictionary(); foreach (ElementId id in elementIds) { Element e = fromDocument.GetElement(id); String name = e.Name; if (!String.IsNullOrEmpty(name)) nameToFromElementsMap.Add(name, id); } // Build a map from name -> target element Dictionary nameToToElementsMap = new Dictionary(); foreach (ElementId id in copiedIds) { Element e = toDocument.GetElement(id); String name = e.Name; if (!String.IsNullOrEmpty(name)) nameToToElementsMap.Add(name, id); } // Merge to make source element -> target element map foreach (String name in nameToFromElementsMap.Keys) { ElementId copiedId; if (nameToToElementsMap.TryGetValue(name, out copiedId)) { elementMap.Add(nameToFromElementsMap[name], copiedId); } } } return elementMap; } /// /// Copies all view-specific elements from a source view to a target view. /// /// /// The source and target views do not have to be in the same document. /// /// The source view. /// The target view. /// The number of new elements created during the copy operation. private static int DuplicateDetailingAcrossViews(View fromView, View toView) { // Collect view-specific elements in source view FilteredElementCollector collector = new FilteredElementCollector(fromView.Document, fromView.Id); // Skip elements which don't have a category. In testing, this was // the revision table and the extents element, which should not be copied as they will // be automatically created for the copied view. collector.WherePasses(new ElementCategoryFilter(ElementId.InvalidElementId, true)); // Get collection of elements to copy for CopyElements() ICollection toCopy = collector.ToElementIds(); // Return value int numberOfCopiedElements = 0; if (toCopy.Count > 0) { using (Transaction t2 = new Transaction(toView.Document, "Duplicate view detailing")) { t2.Start(); // Set handler to skip the duplicate types dialog CopyPasteOptions options = new CopyPasteOptions(); options.SetDuplicateTypeNamesHandler(new HideAndAcceptDuplicateTypeNamesHandler()); // Copy the elements using no transformation ICollection copiedElements = ElementTransformUtils.CopyElements(fromView, toCopy, toView, Transform.Identity, options); numberOfCopiedElements = copiedElements.Count; // Set failure handler to skip any duplicate types warnings that are posted. FailureHandlingOptions failureOptions = t2.GetFailureHandlingOptions(); failureOptions.SetFailuresPreprocessor(new HidePasteDuplicateTypesPreprocessor()); t2.Commit(failureOptions); } } return numberOfCopiedElements; } /// /// Converter delegate for conversion of collections /// /// The view. /// The view's id. private static ElementId ViewConvertToElementId(View view) { return view.Id; } } /// /// A handler to accept duplicate types names created by the copy/paste operation. /// class HideAndAcceptDuplicateTypeNamesHandler : IDuplicateTypeNamesHandler { #region IDuplicateTypeNamesHandler Members /// /// Implementation of the IDuplicateTypeNameHandler /// /// /// public DuplicateTypeAction OnDuplicateTypeNamesFound(DuplicateTypeNamesHandlerArgs args) { // Always use duplicate destination types when asked return DuplicateTypeAction.UseDestinationTypes; } #endregion } /// /// A failure preprocessor to hide the warning about duplicate types being pasted. /// class HidePasteDuplicateTypesPreprocessor : IFailuresPreprocessor { #region IFailuresPreprocessor Members /// /// Implementation of the IFailuresPreprocessor. /// /// /// public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor) { foreach (FailureMessageAccessor failure in failuresAccessor.GetFailureMessages()) { // Delete any "Can't paste duplicate types. Only non duplicate types will be pasted." warnings if (failure.GetFailureDefinitionId() == BuiltInFailures.CopyPasteFailures.CannotCopyDuplicates) { failuresAccessor.DeleteWarning(failure); } } // Handle any other errors interactively return FailureProcessingResult.Continue; } #endregion } }