应用程序:DuplicateViews

Revit平台:所有版本

Revit版本:2014.0

首次发布于:2014.0

编程语言:C#

技能水平:中级

类别:元素,视图

类型:外部应用程序

主题:将草图视图和表格从一个文档复制粘贴到另一个文档

摘要:使用复制粘贴 API 可以将草图视图和表格从一个文档复制到另一个文档。草图视图的内容也将被复制。

相关类:

Autodesk.Revit.UI.IExternalCommand

Autodesk.Revit.DB.ElementTransformUtils

Autodesk.Revit.DB.CopyPasteOptions

Autodesk.Revit.DB.IDuplicateTypeNamesHandler

Autodesk.Revit.DB.FilteredElementCollector

Autodesk.Revit.DB.IFailuresPreprocessor

项目文件:

Application.cs - 实现 IExternalApplication。

DuplicateAcrossDocumentsCommand.cs - 一个用于将表格和草图视图复制到其他文档的命令。

DuplicateViewUtils.cs - 用于促进草图视图和表格复制到另一个文档中的实用工具。

描述:

此示例将当前活动文档中的所有草图视图、图像视图和时间表复制到另一个当前已加载的文档中。同时也将非时间表视图中的所有草图元素复制,以及所有时间表的格式和过滤器。它使用 API 工具隐藏了在通过用户界面执行此操作时通常出现的任何重复类型警告。

说明:

在 Revit 中打开两个项目文档。将活动文档设置为您希望复制视图的文档。选择“插件”->“复制粘贴”->“跨文档复制”。一个弹出消息将列出该命令复制的时间表、草图视图和草图元素的数量。

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

DuplicateAcrossDocumentsCommand.cs

//
// (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.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace Revit.SDK.Samples.DuplicateViews.CS
{
    /// <summary>
    /// A command to copy schedules and drafting views across documents.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    class DuplicateAcrossDocumentsCommand : IExternalCommand
    {
        #region IExternalCommand Members

        /// <summary>
        /// The command implementation.
        /// </summary>
        /// <param name="commandData"></param>
        /// <param name="message"></param>
        /// <param name="elements"></param>
        /// <returns></returns>
        public Result Execute(ExternalCommandData commandData, ref string message, Autodesk.Revit.DB.ElementSet elements)
        {
            Autodesk.Revit.ApplicationServices.Application application = commandData.Application.Application;
            Document doc = commandData.Application.ActiveUIDocument.Document;

            // Find target document - it must be the only other open document in session
            Document toDocument = null;
            IEnumerable<Document> documents = application.Documents.Cast<Document>();
            if (documents.Count<Document>() != 2)
            {
                TaskDialog.Show("No target document",
                                "This tool can only be used if there are two documents (a source document and target document).");
                return Result.Cancelled;
            }
            foreach (Document loadedDoc in documents)
            {
                if (loadedDoc.Title != doc.Title)
                {
                    toDocument = loadedDoc;
                    break;
                }
            }

            // Collect schedules and drafting views
            FilteredElementCollector collector = new FilteredElementCollector(doc);

            List<Type> viewTypes = new List<Type>();
            viewTypes.Add(typeof(ViewSchedule));
            viewTypes.Add(typeof(ViewDrafting));
            ElementMulticlassFilter filter = new ElementMulticlassFilter(viewTypes);
            collector.WherePasses(filter);

            collector.WhereElementIsViewIndependent(); // skip view-specfic schedules (e.g. Revision Schedules);
            // These should not be copied as they are associated to another view that cannot be copied
   
            // Copy all schedules together so that any dependency elements are copied only once
            IEnumerable<ViewSchedule> schedules = collector.OfType<ViewSchedule>();
            DuplicateViewUtils.DuplicateSchedules(doc, schedules, toDocument);
            int numSchedules = schedules.Count<ViewSchedule>();
            
            // Copy drafting views together
            IEnumerable<ViewDrafting> draftingViews = collector.OfType<ViewDrafting>();
            int numDraftingElements = 
                DuplicateViewUtils.DuplicateDraftingViews(doc, draftingViews, toDocument);
            int numDrafting = draftingViews.Count<ViewDrafting>();

            // Show results
            TaskDialog.Show("Statistics",
                   String.Format("Copied: \n" + 
                                "\t{0} schedules.\n" +
                                "\t{1} drafting views.\n"+
                                "\t{2} new drafting elements created.",
                   numSchedules, numDrafting, numDraftingElements));

            return Result.Succeeded;
        }

        #endregion
    }
}

DuplicateViewUtils.cs

//
// (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
{
    /// <summary>
    /// Utilities that facilitate duplication of drafting views and schedules into another document.
    /// </summary>
    class DuplicateViewUtils
    {
        /// <summary>
        /// Utility to duplicate schedules from one document to another.
        /// </summary>
        /// <param name="fromDocument">The source document.</param>
        /// <param name="views">The collection of schedule views.</param>
        /// <param name="toDocument">The target document.</param>
        public static void DuplicateSchedules(Document fromDocument,
                                                    IEnumerable<ViewSchedule> views,
                                                    Document toDocument)
        {
            // Use LINQ to convert to list of ElementIds for use in CopyElements() method
            List<ElementId> ids = 
                views.AsEnumerable<View>().ToList<View>().ConvertAll<ElementId>(ViewConvertToElementId);
            
            // Duplicate.  Pass false to make the function skip returning the map from source element to its copy
            DuplicateElementsAcrossDocuments(fromDocument, ids, toDocument, false);
        }

        /// <summary>
        /// Utility to duplicate drafting views and their contents from one document to another.
        /// </summary>
        /// <param name="fromDocument">The source document.</param>
        /// <param name="views">The collection of drafting views.</param>
        /// <param name="toDocument">The target document.</param>
        /// <returns>The number of drafting elements created in the copied views.</returns>
        public static int DuplicateDraftingViews(Document fromDocument, 
                                                    IEnumerable<ViewDrafting> 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<ElementId> ids = 
                    views.AsEnumerable<View>().ToList<View>().ConvertAll<ElementId>(ViewConvertToElementId);

                // Duplicate.  Pass true to get a map from source element to its copy
                Dictionary<ElementId, ElementId> 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;
        }

        /// <summary>
        /// Duplicates a set of elements across documents.
        /// </summary>
        /// <param name="fromDocument">The source document.</param>
        /// <param name="elementIds">Collection of view ids.</param>
        /// <param name="toDocument">The target document.</param>
        /// <param name="findMatchingElements">True to return a map of matching elements 
        /// (matched by Name).  False to skip creation of this map.</param>
        /// <returns>The map of matching elements, if findMatchingElements was true.</returns>
        private static Dictionary<ElementId, ElementId> DuplicateElementsAcrossDocuments(Document fromDocument,
                                                   ICollection<ElementId> elementIds,
                                                   Document toDocument,
                                                    bool findMatchingElements)
        {
            // Return value
            Dictionary<ElementId, ElementId> elementMap = new Dictionary<ElementId, ElementId>();

            ICollection<ElementId> 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<String, ElementId> nameToFromElementsMap = new Dictionary<string, ElementId>();

                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<String, ElementId> nameToToElementsMap = new Dictionary<string, ElementId>();

                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;
        }

        /// <summary>
        /// Copies all view-specific elements from a source view to a target view.
        /// </summary>
        /// <remarks>
        /// The source and target views do not have to be in the same document.
        /// </remarks>
        /// <param name="fromView">The source view.</param>
        /// <param name="toView">The target view.</param>
        /// <returns>The number of new elements created during the copy operation.</returns>
        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<ElementId> 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<ElementId> 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;
        }

        /// <summary>
        /// Converter delegate for conversion of collections
        /// </summary>
        /// <param name="view">The view.</param>
        /// <returns>The view's id.</returns>
        private static ElementId ViewConvertToElementId(View view)
        {
            return view.Id;
        }

    }

    /// <summary>
    /// A handler to accept duplicate types names created by the copy/paste operation.
    /// </summary>
    class HideAndAcceptDuplicateTypeNamesHandler : IDuplicateTypeNamesHandler
    {
        #region IDuplicateTypeNamesHandler Members

        /// <summary>
        /// Implementation of the IDuplicateTypeNameHandler
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public DuplicateTypeAction OnDuplicateTypeNamesFound(DuplicateTypeNamesHandlerArgs args)
        {
            // Always use duplicate destination types when asked
            return DuplicateTypeAction.UseDestinationTypes;
        }

        #endregion
    }

    /// <summary>
    /// A failure preprocessor to hide the warning about duplicate types being pasted.
    /// </summary>
    class HidePasteDuplicateTypesPreprocessor : IFailuresPreprocessor
    {
        #region IFailuresPreprocessor Members

        /// <summary>
        /// Implementation of the IFailuresPreprocessor.
        /// </summary>
        /// <param name="failuresAccessor"></param>
        /// <returns></returns>
        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
    }
}