Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Add Revit Links to Project with C#

25 REPLIES 25
SOLVED
Reply
Message 1 of 26
Anonymous
5043 Views, 25 Replies

Add Revit Links to Project with C#

Hi all,

 

I'm in my first attempt to create some custom Add-ins for Revit written in C# and the first tool I'm struggling to get working is one that takes some *.rvt files from a folder and links them in the active project.

 

In order to get started I simplified the functionalities of the script and right now I'm just trying to link a file that is hard coded in the script but it is still not working and I'm not too sure on the reason. I suspect it might have to do with the transaction needing to be started and/or closed.

 

This is where I got so far with my code:

 

#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections;
using System.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.Creation;
#endregion

namespace LinkRVT_test
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            // Get application and document objects

            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

            try
            {
                using (Transaction transaction = new Transaction(doc))
                {
                    transaction.Start("Link files");

                    string testFile = @"C: \Users\atassera\AT\C#\Revit\LinkRVT_test\Utilities\AR-GAM-3DM-FACADE-RVT.rvt";

                    ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(testFile);

                    RevitLinkOptions options = new RevitLinkOptions(false);

                    RevitLinkLoadResult result = RevitLinkType.Create(doc, linkpath, options);

                    RevitLinkInstance.Create(doc, result.ElementId);
                    RevitLinkInstance instance2 = RevitLinkInstance.Create(doc, result.ElementId);
                    Location location = instance2.Location;
                    return Result.Succeeded;

                }
            }

When I start debugging, Revit opens and I go in the Add-Ins panel, External Tools and click the tool I'm creating but nothing happens.

 

Any help is greatly appreciated!

 

Thank you

 

Andrea

25 REPLIES 25
Message 2 of 26
Dale.Bartlett
in reply to: Anonymous

Looks to me that you are missing:

transaction.Commit();

before the

return Result.Succeeded;

 

Dale




______________
Yes, I'm Satoshi.
Message 3 of 26
Anonymous
in reply to: Dale.Bartlett

Thank you Dale,

 

Your suggestion actually moved something, but the script is still not working.

 

Now what happens is that Revit starts calculating and it says it is "Loading linked document" with the usual progress bar at the bottom left of the window bu then nothing is done and if you go in the Manage links window you see nothing has been added.

 

Let me re-copy the script below with the amendment you suggested  and another small one I just noticed:

 

#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections;
using System.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.Creation;
#endregion

namespace LinkRVT_test
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            // Get application and document objects

            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

            try
            {
                using (Transaction transaction = new Transaction(doc))
                {
                    transaction.Start("Link files");

                    string testFile = @"C:\Users\atassera\AT\C#\Revit\LinkRVT_test\Utilities\AR-GAM-3DM-FACADE-RVT.rvt";

                    ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(testFile);

                    RevitLinkOptions options = new RevitLinkOptions(false);

                    RevitLinkLoadResult result = RevitLinkType.Create(doc, linkpath, options);

                    RevitLinkInstance.Create(doc, result.ElementId);
                    RevitLinkInstance instance2 = RevitLinkInstance.Create(doc, result.ElementId);
                    Location location = instance2.Location;

                    transaction.Commit();
                    return Result.Succeeded;
                }
            }

Any idea what is going on there and why at some point it just stops doing what it its supposed to do?

 

Thanks!

 

Andrea

Message 4 of 26
so-chong
in reply to: Anonymous

Hi, your catch-finally block is missing in your code.

 

Thats maybe why your code is not running?

 

Hope this helps.

 

Cheers,

So-Chong

try_catch_block-statement.png

Message 5 of 26
jeremytammik
in reply to: so-chong

A `finally` clause with nothing in it does exactly what you are specifying: nothing.

 

🙂

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 6 of 26
jeremytammik
in reply to: Anonymous

Why are you calling `RevitLinkInstance.Create` twice over with the exact same arguments?

 

That looks weird to me.

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 7 of 26
so-chong
in reply to: jeremytammik

Hi Jeremy, i totally agree with your comment 😉

@Anonymous; you can remove the 'finally' clause if you don't specify what it have to do.

 

Interesting though, the Sharpdevelop editor gives an error without the modified code.

I wonder why Visual Studio(VS) is not complaining or gives an error about the try-catch clause if it was compiled succesfully in VS...or did it?

i have not tested in VS yet.

 

Cheers,

So-chong

 

Message 8 of 26
Anonymous
in reply to: jeremytammik

Hi @jeremytammik , thank you for your reply!

 

Yeah it was looking odd to me as well, but I fount it in the example code in the Revit API Docs website and I took it as it was...I guess they were probably showing three different possibilities?
from Revit API Docfrom Revit API Doc

 

Anyways, I deleted that part (latest code copied below) but it is still not working...again, it starts calculating and it gives the progress bar saying it is doing something with that file but then nothing happens. What am I doing wrong??

 

#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections;
using System.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.Creation;
#endregion

namespace LinkRVT_test
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            // Get application and document objects

            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

            try
            {
                using (Transaction transaction = new Transaction(doc))
                {
                    transaction.Start("Link files");

                    string testFile = @"C:\Users\atassera\AT\C#\Revit\LinkRVT_test\Utilities\AR-GAM-3DM-FACADE-RVT.rvt";

                    ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(testFile);

                    RevitLinkOptions options = new RevitLinkOptions(false);

                    RevitLinkLoadResult result = RevitLinkType.Create(doc, linkpath, options);

                    RevitLinkInstance.Create(doc, result.ElementId);

                    transaction.Commit();
                    return Result.Succeeded;
                }
            }

            catch (Autodesk.Revit.Exceptions.OperationCanceledException)
            {
// If user decided to cancel the operation return Result.Canceled

                return Result.Cancelled;
            }

            catch (Exception ex)
            {
// If something went wrong return Result.Failed

                Console.WriteLine("There was a problem!");
                Console.WriteLine(ex.Message);
                return Result.Failed;
            }
        }
    }
}

 

@so-chong I actually have two catch clauses (more as place holders at the moment) but I thought they didn't really make a difference and I didn't copy them here as they would have made the code longer...I copied them now for clarity.

 

Thanks guys, any suggestion and clarification is very well appreciated!

 

Cheers,

 

Andrea

Message 9 of 26
Anonymous
in reply to: Anonymous

Hi all,

 

I just figured out that the script was not working because the file was getting corrupted for some reason while being upgraded. Sorry my bad, I guess I still need to learn a lot here both about Revit API and workflows development.

 

But I guess the real solution was the one from @Dale.Bartlett when he spotted I needed to "commit" the transaction. On the regards, does the "transaction.Commit();" line means something like "end the transaction"?

 

Thank you again!

 

Andrea

Message 10 of 26
Dale.Bartlett
in reply to: Anonymous

As the name suggests, it Commits the transaction. .Rollback does just that. A transaction is like an AutoCAD Undo group. Note there is also a TransactionGroup, which groups transactions into one undo. Looks like it was a great learning exercise for you, well done. Dale




______________
Yes, I'm Satoshi.
Message 11 of 26
jeremytammik
in reply to: Anonymous

The sample code you show in your snapshot is from an old version of the Revit API docs.

 

This is what it lists today; one single call to Create:

 

Examples

 CopyC#
public ElementId CreateRevitLink(Document doc, string pathName)
{
    FilePath path = new FilePath(pathName);
    RevitLinkOptions options = new RevitLinkOptions(false);
    // Create new revit link storing absolute path to a file
    LinkLoadResult result = RevitLinkType.Create(doc, path, options);
    return (result.ElementId);
}

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 12 of 26

For completeness, my 2017 requires:

RevitLinkLoadResult result = RevitLinkType.Create(doc, path, options);



______________
Yes, I'm Satoshi.
Message 13 of 26
Anonymous
in reply to: Dale.Bartlett

Thanks @Dale.Bartlett and @jeremytammik,

 

I guess what I posted is old because I am developing that tool for Revit 2017.

 

I actually have another question, which I tried to solve by searching here on the forum and elsewhere but I'm so far from understanding that I don't even understand where to start so any input is very welcome:

 

Now, instead of hardcoding the file path or the folder path where all the files are, I'm trying to prompt the user with a file browser where he can select the files that he wants to link in the project.

 

I've seen in another post here that @jeremytammik suggested this class: https://docs.microsoft.com/en-au/dotnet/api/system.windows.forms.openfiledialog?view=netframework-4....

 

but I'm not sure it's the case here as I don't want to open any file but just retrieve the file path so I can use them in the script I already have so they can be linked in the project.

 

I'm sorry, I know it can be considered off-topic in this thread as it is sort of a different issue. I wanted to open a new thread but I don't even know how to call it and make it relevant.

 

Thanks

Message 14 of 26
jeremytammik
in reply to: Anonymous

Once again, you are in luck.

 

I implemented a BrowseDirectory utility method using the .NET FolderBrowserDialog function for my ExportCncFab add-in:

 

http://thebuildingcoder.typepad.com/blog/2013/12/driving-cnc-fabrication-and-shared-parameters.html

 

https://github.com/jeremytammik/ExportCncFab

 

https://github.com/jeremytammik/ExportCncFab/blob/master/ExportCncFab/Util.cs

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 15 of 26
stever66
in reply to: Dale.Bartlett


@Dale.Bartlett wrote:

For completeness, my 2017 requires:

RevitLinkLoadResult result = RevitLinkType.Create(doc, path, options);

That will change in 2018:

The class RevitLinkLoadResult has been renamed to LinkLoadResult. Note that applications referring to RevitLinkLoadResult will need to be recompiled using the new name. The class is otherwise unchanged. The new CADLinkType reload and creation functions will return a LinkLoadResult object.

 

Sometimes I think they rename these things just to keep us on our toes. 🙂

 

Message 16 of 26
Anonymous
in reply to: jeremytammik

Thanks @jeremytammik ,

 

Always great work!

 

What I'm actually trying to do though is to get a file browser window popping up for the user to select which files he wants to link in the Revit project, rather than simply prompting him to select the folder and then getting all the files inside.

 

I'm trying to use the OpenFileDialog class right now as I've seen it is possible to get the file paths with that.

 

Ideally I would prompt the user with a file browser (as soon as he clicks the icon of the tool), get the file paths of the files he selected and feed them in the RevitLinkInstance part.

 

This is my code, which is evidently wrong but I don't know how to fix:

 

#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections;
using System.Linq;
using System.Windows.Forms;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.Creation;
#endregion

namespace LinkRVT_test
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            // Get application and document objects

            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

            try
            {
                using (Transaction transaction = new Transaction(doc))
                {
                    // Link files in folder

                    transaction.Start("Link files");

                    OpenFileDialog openFileDialog1 = new OpenFileDialog();
                    openFileDialog1.InitialDirectory = (@"P:\");
                    openFileDialog1.Filter = "RVT|*.rvt";
                    openFileDialog1.Multiselect = true;
                    openFileDialog1.RestoreDirectory = true;

                    if (openFileDialog1.ShowDialog() == DialogResult.OK)
                    {
                        string[] filesInFolder = openFileDialog1.FileNames;
                    }

                    foreach (string path in filesInFolder)
                    {
                        ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(path);
                        RevitLinkOptions options = new RevitLinkOptions(false);
                        RevitLinkLoadResult result = RevitLinkType.Create(doc, linkpath, options);
                        RevitLinkInstance.Create(doc, result.ElementId);
                    }

                    // Show summary message

                    //TaskDialog.Show("Files", filePaths.ToString());

                    // Assuming that everything went right return Result.Succeeded

                    transaction.Commit();
                    return Result.Succeeded;

                }
            }

            catch (Autodesk.Revit.Exceptions.OperationCanceledException)
            {
            // If user decided to cancel the operation return Result.Canceled

                return Result.Cancelled;
            }

            catch (Exception ex)
            {
            // If something went wrong return Result.Failed

                Console.WriteLine("There was a problem!");
                Console.WriteLine(ex.Message);
                return Result.Failed;
            }
        }

One of the problems is that filesInFolder is declared in the if statement so the subsequent foreach statement can't get the values. But apart from that I believe there is some very fundamental error that I'm not getting. What am I doing wrong?

 

Thanks for the help!

 

Andrea

Message 17 of 26
Dale.Bartlett
in reply to: Anonymous

One of the problems is that filesInFolder is declared in the if statement so the subsequent foreach statement can't get the values.

 

Yes, this will not work; declare it outside the foreach. Have you checked the contents of fileInFolder before attempting to process?

foreach (string path in filesInFolder)
{
MessageBox.Show("Path = " + path);
}

 

Dale




______________
Yes, I'm Satoshi.
Message 18 of 26
Anonymous
in reply to: Dale.Bartlett

Thanks @Dale.Bartlett;

 

Does it mean that I can put the line string[] filesInFolder = openFileDialog1.FileNames; outside the if (openFileDialog1.ShowDialog() == DialogResult.OK){ }

like this?

                    OpenFileDialog openFileDialog1 = new OpenFileDialog();
                    openFileDialog1.InitialDirectory = (@"P:\");
                    openFileDialog1.Filter = "RVT|*.rvt";
                    openFileDialog1.Multiselect = true;
                    openFileDialog1.RestoreDirectory = true;

                    string[] filesInFolder = openFileDialog1.FileNames;

                    foreach (string path in filesInFolder)
                    {
                        ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(path);
                        RevitLinkOptions options = new RevitLinkOptions(false);
                        RevitLinkLoadResult result = RevitLinkType.Create(doc, linkpath, options);
                        RevitLinkInstance.Create(doc, result.ElementId);
                    }

Then, I tried doing a TaskDialog.Show("Files", filesInFolder.ToString()); to see if something goes in the filesInFolder variable but the message that comes out says "System.String[] (img below). Because Revit is not prompting me with the file browser nothing goes through the array filesInFolder.

filesInFolder_empty.PNG

 

Could it be because the whole OpenFileDialog needs to be inside a method on its own? I've seen in some examples that usually it comes in its own method (often as the event triggered by a button) as it is used as a helper for a form. What I thought for my own case is that I don't really need a form and a button that triggers the file browser to appear as what I'm wishing to achieve is for the file browser to pop up as the first thing when you click on the icon of the tool. 

 

PS when I mention the file browser that I want to appear I mean something like this where I can select the files I want to link....just to clarify in case I'm using the wrong jargon

Image result for file browser

Message 19 of 26
Dale.Bartlett
in reply to: Anonymous

Try this, bit rushed and untested, but may get you going. I can see you are really trying. Good luck.

public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            // Get application and document objects
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Autodesk.Revit.DB.Document doc = uidoc.Document;

            try
            {
                using (Transaction transaction = new Transaction(doc))
                {
                    // Link files in folder
                    transaction.Start("Link files");

                    OpenFileDialog openFileDialog1 = new OpenFileDialog();
                    openFileDialog1.InitialDirectory = (@"P:\");
                    openFileDialog1.Filter = "RVT|*.rvt";
                    openFileDialog1.Multiselect = true;
                    openFileDialog1.RestoreDirectory = true;

                    if (openFileDialog1.ShowDialog() == DialogResult.OK)
                    {
                        // string[] filesInFolder = openFileDialog1.FileNames;
                        foreach (string path in openFileDialog1.FileNames)
                        {
                            FileInfo filePath = new FileInfo(path);

                            // debug ***********
                            MessageBox.Show("filePath.FullName.ToString() = " + filePath.FullName.ToString());
                            // debug ***********

                            ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(filePath.FullName.ToString());
                            RevitLinkOptions options = new RevitLinkOptions(false);
                            RevitLinkLoadResult result = RevitLinkType.Create(doc, linkpath, options);
                            RevitLinkInstance.Create(doc, result.ElementId);
                        }
                    }
                    // Show summary message
                    //TaskDialog.Show("Files", filePaths.ToString());
                    // Assuming that everything went right return Result.Succeeded
                    transaction.Commit();
                    return Result.Succeeded;
                }
            }
            catch (Autodesk.Revit.Exceptions.OperationCanceledException)
            {
                // If user decided to cancel the operation return Result.Canceled
                return Result.Cancelled;
            }
            catch (Exception ex)
            {
                // If something went wrong return Result.Failed
                Console.WriteLine("There was a problem!");
                Console.WriteLine(ex.Message);
                return Result.Failed;
            }
        }

 

Dale




______________
Yes, I'm Satoshi.
Message 20 of 26
Anonymous
in reply to: Dale.Bartlett

Wow, thanks @Dale.Bartlett , it worked perfectly!

 

Just a couple of questions to be sure I'm getting this right:

1) is this line the one responsible to actually pop up the file browser?

                    if (openFileDialog1.ShowDialog() == DialogResult.OK)

 

2) openFileDialog1 becomes sort of a variable that stores the input from the user? Otherwise where is the selection stored?

 

Thanks a lot for the help!

 

Andrea

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Customer Advisory Groups


Rail Community