Announcements

Starting in December, we will archive content from the community that is 10 years and older. This FAQ provides more information.

Trying to copy paste a Revit element using elementID

ivas0016
Contributor
Contributor

Trying to copy paste a Revit element using elementID

ivas0016
Contributor
Contributor

I am trying to copy a specific element beside another element the user selects. My code is saying that its copied, but I cannot see the new element in Revit. Please help!


using System;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.DB.Structure;

namespace RevitCommands
{
[Transaction(TransactionMode.Manual)]
public class AddAccessControlReader : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
// Get the Revit application and document
UIApplication uiApp = commandData.Application;
UIDocument uiDoc = uiApp.ActiveUIDocument;
Document doc = uiDoc.Document;

// Start a new transaction
using (Transaction transaction = new Transaction(doc, "Add Access Control Reader"))
{
try
{
// Start the transaction
transaction.Start();

// Select an element using the Revit UI selection mechanism
Reference elementRef = uiDoc.Selection.PickObject(ObjectType.Element, "Select an element");
Element selectedElement = doc.GetElement(elementRef);

// Check if the selected element is valid
if (selectedElement == null)
{
TaskDialog.Show("Error", "No element selected or element not found.");
transaction.RollBack();
return Result.Failed;
}

// Find the family instance to be copied
ElementId sourceElementId = new ElementId(1250743);
FamilyInstance sourceInstance = doc.GetElement(sourceElementId) as FamilyInstance;

if (sourceInstance == null)
{
TaskDialog.Show("Error", "Source element not found.");
transaction.RollBack();
return Result.Failed;
}

// Get the location of the selected element
LocationPoint elementLocation = selectedElement.Location as LocationPoint;
if (elementLocation == null)
{
TaskDialog.Show("Error", "Selected element does not have a valid location.");
transaction.RollBack();
return Result.Failed;
}
XYZ elementPosition = elementLocation.Point;

// Create a copy of the source instance at an offset position
XYZ instancePosition = elementPosition + new XYZ(0.1, 0, 0);

// Find a suitable level for placing the copied instance
Level targetLevel = doc.GetElement(selectedElement.LevelId) as Level;

FamilyInstance copyInstance = doc.Create.NewFamilyInstance(instancePosition, sourceInstance.Symbol, targetLevel, StructuralType.NonStructural);

if (copyInstance != null)
{
// Commit the transaction
transaction.Commit();

TaskDialog.Show("Success", "Element copy added successfully.");
return Result.Succeeded;
}
else
{
TaskDialog.Show("Error", "Failed to copy the element.");
transaction.RollBack();
return Result.Failed;
}
}
catch (Exception ex)
{
// Rollback the transaction
transaction.RollBack();
TaskDialog.Show("Error", "An error occurred: " + ex.Message);
return Result.Failed;
}
}
}
}
}

0 Likes
Reply
611 Views
8 Replies
Replies (8)

jeremy_tammik
Autodesk
Autodesk

It should be simple. However, your code is long and hard to read. First of all, I would suggest to restructure it a little by prompting the user to select the element and validating all  prerequisites in read-only mode, before starting the transaction. That will significantly simplify and shorten your code. Furthermore, it will be more readable if you paste it into the conversation using the 'Insert Code Sample' button above, marked </>.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes

sragan
Collaborator
Collaborator

Having a copied element that's not visible is a common problem even when using the user interface.  Make sure its just not hidden somewhere.

 

Maybe select the original element, and right click, and pick "select all instances" in project and see if the number selected goes up by one after running your command.

 

sragan_0-1688127658692.png

 

0 Likes

RPTHOMAS108
Mentor
Mentor

Perhaps it is outside the view range i.e. what was the original Z and how is that defined by level and offset compared to what is reported by location point?

 

However seems what you are doing can be done far more easily with 

ElementTransformUtils.CopyElement

That then just requires a translation vector.

 

Even if you want to insert a different type I'd say change the type after copying an instance.

ivas0016
Contributor
Contributor

You are spot on. I got in touch with someone on my Revit team and that was the case. I tried ElementTransformUtils per your suggestion and it solved the problem. 

0 Likes

ivas0016
Contributor
Contributor

I did as you suggested, changing the type ID later on, however it only works with some elements. I am trying to copy paste a security device beside a door. So first I copy pasted a door and then i tried to change it into a security devices, but in this instance my code doesnt work. Any ideas?

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Linq;

namespace RevitCommands
{
    [Transaction(TransactionMode.Manual)]
    public class AddAccessControlReader : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            // Get the Revit document
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Document doc = uidoc.Document;

            try
            {
                // Select the source element to be copied
                Reference sourceElementRef = uidoc.Selection.PickObject(ObjectType.Element, "Select the element to be copied");
                Element sourceElement = doc.GetElement(sourceElementRef);

                // Specify the distance
                double distanceInM_x = 1;
                double distanceConvert_x = UnitUtils.ConvertToInternalUnits(distanceInM_x, DisplayUnitType.DUT_METERS);

                double distanceInM_y = 1;
                double distanceConvert_y = UnitUtils.ConvertToInternalUnits(distanceInM_y, DisplayUnitType.DUT_METERS);

                double distanceInM_z = 1;
                double distanceConvert_z = UnitUtils.ConvertToInternalUnits(distanceInM_z, DisplayUnitType.DUT_METERS);

                // Calculate the translation vector
                XYZ translationVector = new XYZ(distanceConvert_x, distanceConvert_y, distanceConvert_z);

                Element newElement = null; // Declare the variable here before starting any transactions

                using (TransactionGroup tg = new TransactionGroup(doc))
                {
                    tg.Start("Copy and Change Element Type");

                    using (Transaction copyTransaction = new Transaction(doc, "Copy Element"))
                    {
                        copyTransaction.Start();

                        // Duplicate the source element using ElementTransformUtils.CopyElement with the translation vector
                        ElementId newElementId = ElementTransformUtils.CopyElement(doc, sourceElement.Id, translationVector).First();

                        // Get the newly created element
                        newElement = doc.GetElement(newElementId); // Assign the value here

                        copyTransaction.Commit();
                    }

                    using (Transaction typeChangeTransaction = new Transaction(doc, "Change Element Type"))
                    {
                        typeChangeTransaction.Start();

                        // Select the desired element type
                        Reference desiredTypeRef = uidoc.Selection.PickObject(ObjectType.Element, "Select the desired element type");
                        Element desiredTypeElement = doc.GetElement(desiredTypeRef);

                        // Change the element type using the ChangeTypeId method
                        ElementId desiredTypeId = desiredTypeElement.GetTypeId();
                        newElement.ChangeTypeId(desiredTypeId);

                        typeChangeTransaction.Commit();
                    }

                    using (Transaction deleteTransaction = new Transaction(doc, "Delete Source Element"))
                    {
                        deleteTransaction.Start();

                        // Delete the source element
                        doc.Delete(sourceElement.Id);

                        deleteTransaction.Commit();
                    }

                    tg.Assimilate();
                }

                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return Result.Failed;
            }
        }
    }
}
0 Likes

RPTHOMAS108
Mentor
Mentor

They are of two different categories so yes I doubt that would work.

 

Seems you have to go back to your initial approach but with correct level.

ivas0016
Contributor
Contributor
But how would I do that? Like I mentioned before I am completely new to
Revit so the whole Idea that some instances are positioned according to the
level and some according to the axis origin is weird. I am not sure how I
would go about this?
0 Likes

RPTHOMAS108
Mentor
Mentor

You have used one of the level based overloads but perhaps you need to investigate how it would be placed in the UI and select a better overload.  It is unfortunate you don't have experience in Revit but as a minimum you need to understand the concepts surrounding how family instances are placed manually in Revit to decide what overload to use with the Revit API. This is really for a developer no different to programming with any other API, you need basic knowledge of the associated 'Application' part. Otherwise all I can suggest is perhaps you have a discussion with your Revit expert and go through the overloads with them for the family involved.

 

Doors are hosted in walls and security devices could be hosted or not 

 

ItemFactoryBase.NewFamilyInstance(XYZ, FamilySymbol, Element, StructuralType)

This can be used for hosting doors/windows in walls and there is an example of such in the RevitAPI.chm

 

What you use for security device depends on the family template used to create it and settings of such. With these overloads you often have to experiment with what works and ensure you are not getting odd behaviour that you wouldn't get in the UI. When you do it often points to using the wrong overload.

 

Typically the overloads are split into

Hosting on element face (containing reference or face argument)

Hosting on element (contains HostElement argument)

Hosting on level (containing Level argument)

View specific elements (containing view argument)