Creating C# script for a button to insert a detail component

Creating C# script for a button to insert a detail component

j-kirkpatrick
Enthusiast Enthusiast
5,352 Views
18 Replies
Message 1 of 19

Creating C# script for a button to insert a detail component

j-kirkpatrick
Enthusiast
Enthusiast

Hello all,

 

So lately I've been using pyRevit to try and complete this however the API just isn't clicking with me and i'm not a coder by nature. I've tried finding examples, of which I have found only 1 I think that may be close to what I'm wanting but I have no way to confirm. I am extremely familiar with revit and I can make my way through coding even without fully knowing it.

 

This is the example I've found.

 

UIApplication application = commandData.Application;
UIDocument uiDocument = application.ActiveUIDocument;
Document document = application.ActiveUIDocument.Document;

FilteredElementCollector familyCollector = new FilteredElementCollector(document);familyCollector.OfClass(typeof(FamilySymbol));

FamilySymbol familySymbolToFind = null;

foreach (FamilySymbol familySymbol in familyCollector)
{
  //To search by FamilySymbol name
  if (familySymbol.Name == "[Name of FamilySymbol to find]")    familySymbolToFind = familySymbol;
  //To search by Family name
  else if (familySymbol.Family.Name = "[Name of Family to find]")    familySymbolToFind = familySymbol;
}uiDocument.PromptForFamilyInstancePlacement(familySymbolToFind);

The pyRevit (python) examples i've followed seem rather simple, but it's just not clicking. How does this declare for example if I wanted it to be a "break line" that is already loaded into a template. This was there's no searching. 

 

I already have the toolbar set up so I have my own ribbon tab and push button. Once I can get this to work then the next step is to create drop down lists for W beams by section/plan/etc.

0 Likes
5,353 Views
18 Replies
Replies (18)
Message 2 of 19

stever66
Advisor
Advisor

I believe that example assumes the correct family is already loaded into the revit project file.   If you want to place items that haven't been loaded yet, you will have to check to see if the item has been loaded, and if not, load the family.

 

There are a couple of helpful examples here:

 

http://www.thebuildingcoder.typepad.com/blog/2013/06/family-api-add-in-load-family-and-place-instanc...

 

I also notice in you r else if statement, you need a double equal instead of a single equal.  They mean different things;

 

change:  else if (familySymbol.Family.Name = "name")    familySymbolToFind = familySymbol;

to:   else if (familySymbol.Family.Name == "name")    familySymbolToFind = familySymbol;

 

 

0 Likes
Message 3 of 19

stever66
Advisor
Advisor

Try something like this:


         FilteredElementCollector familyCollector = new FilteredElementCollector(document);familyCollector.OfClass(typeof(FamilySymbol));

        FamilySymbol familySymbolToFind = null;

        foreach (FamilySymbol familySymbol in familyCollector)
            {
              //To search by FamilySymbol name
             // TaskDialog.Show("Revit", familySymbol.Name.ToString());
              if (familySymbol.Name == "Breakline")    familySymbolToFind = familySymbol;
              //To search by Family name
              else if (familySymbol.Family.Name == "Breakline")    familySymbolToFind = familySymbol;
              //TaskDialog.Show("Revit", familySymbol.Family.Name.ToString());
            }

        if (familySymbolToFind == null)
            using( Transaction tx = new Transaction( document ) )
            {
                 tx.Start"Load Family" );
                  document.LoadFamilySymbol@"C:\Temp\Breakline.rfa""Breakline" );
                  TaskDialog.Show("Revit""family loaded");
                  tx.Commit();
                  
                  foreach (FamilySymbol familySymbol in familyCollector)
                    {
                      //To search by FamilySymbol name
                     if (familySymbol.Name == "Breakline")    familySymbolToFind = familySymbol;
                      //To search by Family name
                      else if (familySymbol.Family.Name == "Breakline")    familySymbolToFind = familySymbol;
                      
                    }
                  
            }

            try
            {
                    uiDocument.PromptForFamilyInstancePlacement(familySymbolToFind);
            }
        
            catch
            {
                TaskDialog.Show("Revit C# Error""Error rotating elements.");
            }
             

 

I ran this in the macro manager, and it worked.  I left off your first 3 lines of code because they are a little different in the macro manager.

 

Notice that anytime you change the revit database, you are supposed to do it within a "transaction" using the t.start and t.commit lines  of code.  That's how revit makes sure only valid changes are saved, and how it rolls back changes with the "undo" button.

 

However, it leaves me baffled how I can do the PromptForFamilyInstancePlacement in the macro without having it within a transaction.  In fact, if I try to wrap it inside a transaction, I get an error message. 

 

So let me know how this works when its placed in a add-in file.  I suspect you may need to create another transaction for the placement method, or change it so the transaction is not committed until after this line of code.
         
         

0 Likes
Message 4 of 19

j-kirkpatrick
Enthusiast
Enthusiast

That's the tricky part I'm having as an issue is that I don't know C#. I spent my time learning python due to how much more simple writing is. I have a RPS and RPW in revit that basically lets me write in python and save scripts and apply to buttons. So as for the transactions honestly I am not even sure what C# means by those yet.

 

For reference this is what I have been learning and going by:

https://www.youtube.com/watch?v=PfCn7OrY_-A&list=PLc_1PNcpnV5742XyF8z7xyL9OF8XJNYnv&index=3 

 

If you start checking that out and see the toolbar he has made, which is amazing in itself and I use it every day at work, I think everyone could use this honestly. I just don't know how to make this into python. I'll give it a shot and see what I come up with. Thank you for your responses!

0 Likes
Message 5 of 19

stever66
Advisor
Advisor

The built in Revit macro editor has a converter.  I believe Visual Studio has the same thing.  The converter didn't like the "using transaction" statement, but here is another way to start a transaction.   Here it is as a python macro using the built in editor:

 

uiDocument = ActiveUIDocument
        document = ActiveUIDocument.Document
        #Autodesk.Revit.DB.Transaction  t = new Autodesk.Revit.DB.Transaction(doc, "Place Element");
        #t.Start();
        familyCollector = FilteredElementCollector(document)
        familyCollector.OfClass(clr.GetClrType(FamilySymbol))
        familySymbolToFind = None
        enumerator = familyCollector.GetEnumerator()
        while enumerator.MoveNext():
            familySymbol = enumerator.Current
            #To search by FamilySymbol name
            # TaskDialog.Show("Revit", familySymbol.Name.ToString());
            if familySymbol.Name == "Breakline":
                familySymbolToFind = familySymbol
            elif familySymbol.Family.Name == "Breakline":
                familySymbolToFind = familySymbol
        #TaskDialog.Show("Revit", familySymbol.Family.Name.ToString());
        if familySymbolToFind == None:
            t = Autodesk.Revit.DB.Transaction(document, "Place Family")
            t.Start("Load Family")
            document.LoadFamilySymbol("C:\Temp\Breakline.rfa""Breakline")
            TaskDialog.Show("Revit""family loaded")
            t.Commit()
            enumerator = familyCollector.GetEnumerator()
            while enumerator.MoveNext():
                familySymbol = enumerator.Current
                #To search by FamilySymbol name
                if familySymbol.Name == "Breakline":
                    familySymbolToFind = familySymbol
                elif familySymbol.Family.Name == "Breakline":
                    familySymbolToFind = familySymbol
        try:
            uiDocument.PromptForFamilyInstancePlacement(familySymbolToFind)
        except:
            TaskDialog.Show("Revit C# Error""Error placing elements.")

 

Again, you might have to do some tweaking with the transactions to get it to work as an external add-in.  There are some python examples that include transactions here;

 

http://wiki.theprovingground.org/revit-api

 

0 Likes
Message 6 of 19

stever66
Advisor
Advisor

I just found out why the PromptForFamilyInstancePlacement can not be placed inside a transaction.  From Jeremy's blog:

 

This method opens its own transaction, so it's not permitted to be invoked in an active transaction. In a single invocation, the user can place multiple instances of the input family type until they finish the placement (with Cancel or ESC or a click elsewhere in the UI). The user will not be permitted to change the type to be placed. Users are not permitted to change the active view during this placement operation (the operation will be completed).

 

So the Python code I posted should work in visual studio also.

 

Message 7 of 19

j-kirkpatrick
Enthusiast
Enthusiast

That's very interesting, when I have time this weekend I'm going to start learning the syntax for C#. Just so continued process of this will be easier. Currently I'm trying to work in testing these as I'm working. Thank you so much for your help so far. You literally have no idea how much help you've been.

0 Likes
Message 8 of 19

j-kirkpatrick
Enthusiast
Enthusiast

So this is where i'm at with it and I keep getting an error on line 44. I even took the breakline file which is named BNI_BREAKLINE, and set the names of the code to match that and even put a copy in the temp location so it should be able to find it.

See the link for error msg

https://imgur.com/22NiYoQ 

 

Now I do have a question with this. I don't need to import the families, due to have a template set up with a large number of annotative/detail components loaded into the template. So is it necessary to load the family? Or is that how the code loads the family to be placed?

 

 

from Autodesk.Revit.DB import *

from Autodesk.Revit.DB.Architecture import *

from Autodesk.Revit.DB.Analysis import *

from Autodesk.Revit.UI import *

 

import clr

# clr.AddReferenceByName("PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")

# clr.AddReferenceByName("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")

clr.AddReferenceByPartialName('PresentationCore')

clr.AddReferenceByPartialName("PresentationFramework")

clr.AddReferenceByPartialName('System.Windows.Forms')

import System.Windows

 

uidoc = __revit__.ActiveUIDocument

doc = __revit__.ActiveUIDocument.Document

selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]

 

def alert(msg):

                TaskDialog.Show('pyRevit', msg)

 

def quit():

                __window__.Close()

 

if len(selection) > 0:

                el = selection[0]

 

 

 

 

from Autodesk.Revit.DB import FilteredElementCollector

 

 

        #Autodesk.Revit.DB.Transaction  t = new Autodesk.Revit.DB.Transaction(doc, "Place Element");

        #t.Start();

 

familyCollector = FilteredElementCollector(doc)

familyCollector.OfClass(clr.GetClrType(FamilySymbol))

familySymbolToFind = None

enumerator = familyCollector.GetEnumerator()

while enumerator.MoveNext():

                familySymbol = enumerator.Current

            #To search by FamilySymbol name

            #TaskDialog.Show("Revit", familySymbol.Name.ToString());

                if familySymbol.Name == "BNI_BREAKLINE":

                                familySymbolToFind = familySymbol

                elif familySymbol.Family.Name == "BNI_BREAKLINE":

                                familySymbolToFind = familySymbol

        #TaskDialog.Show("Revit", familySymbol.Family.Name.ToString());

                if familySymbolToFind == None:

                                t = Autodesk.Revit.DB.Transaction(doc, "Place Family")

                                t.Start("Load Family")

                                doc.LoadFamilySymbol("C:\Temp\BNI_BREAKLINE.rfa", "BNI_BREAKLINE")

                                TaskDialog.Show("Revit", "family loaded")

                                t.Commit()

                                enumerator = familyCollector.GetEnumerator()

                                while enumerator.MoveNext():

                                                familySymbol = enumerator.Current

                #To search by FamilySymbol name

                                                if familySymbol.Name == "BNI_BREAKLINE":

                                                                familySymbolToFind = familySymbol

                                                elif familySymbol.Family.Name == "BNI_BREAKLINE":

                                                                familySymbolToFind = familySymbol

 

uidoc.PromptForFamilyInstancePlacement(familySymbolToFind)

0 Likes
Message 9 of 19

stever66
Advisor
Advisor

Glad I could be of help.  I'm learning also as we go.

 

Regarding the error, I had a family called "Breakline" with a family type also called "Breakline". 

 

If you have a family that doesn't have any types (called "Family Symbols" in the API), you might try adding a new type called "BNI_Breakline".

 

There is also a "LoadFamily" command that might work.  It's mentioned here:

 

http://www.thebuildingcoder.typepad.com/blog/2013/06/family-api-add-in-load-family-and-place-instanc...

 

breakline image.png

 

 

 

 

 

 

0 Likes
Message 10 of 19

j-kirkpatrick
Enthusiast
Enthusiast

So I double checked there definitely is a type for this in the family. However whats weird, is in the code i'm not seeing a specific place for it to say it is loading the type and not just the family. As well how do you add a type to the Revit API. I looked through the link but I am not seeing anything towards what I am needing. Is it something like in the beginning of the code saying import?

 

I'll try making it the same family symbol as type and see what happens. The problem is going to come as well when try and make a drop down for W beams, they'll be different types, same family though.

0 Likes
Message 11 of 19

j-kirkpatrick
Enthusiast
Enthusiast

So I have gotten this to load the family finally. However now it is saying

Exception : System.MissingMemberException: Name

I've tried adding the T = autodesk... but it says can not create new transaction but if you have it how it do it says sure for some reason. it seems like its working through everything except placing the instance. I removed the transaction on line 47 and the commit. This is as far as I have been able to get it.

 

 

 

from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import *
from Autodesk.Revit.DB.Analysis import *
from Autodesk.Revit.UI import *

import clr
# clr.AddReferenceByName("PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
# clr.AddReferenceByName("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
clr.AddReferenceByPartialName('PresentationCore')
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName('System.Windows.Forms')
import System.Windows

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]

def alert(msg):
    TaskDialog.Show("pyRevit", msg)

def quit():
    __window__.Close()

if len(selection) > 0:
    el = selection[0]




from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

#t = Transaction(doc, "Place Element")
#Autodesk.Revit.DB.Transaction  
#t.Start()

familyCollector = FilteredElementCollector(doc)
familyCollector.OfClass(clr.GetClrType(FamilySymbol))
familySymbolToFind = None
enumerator = familyCollector.GetEnumerator()
while enumerator.MoveNext():
    familySymbol = enumerator.Current

#To search by FamilySymbol name
# TaskDialog.Show("Revit", familySymbol.Name.ToString());

    if familySymbolToFind == None:
        Transaction(doc, "Place Family").Start("Load Family")
        doc.LoadFamilySymbol("C:\Temp\Break Line.rfa""Break Line")
    TaskDialog.Show("Revit""family loaded")
    enumerator = familyCollector.GetEnumerator()
    while enumerator.MoveNext():
            familySymbol = enumerator.Current

#To search by FamilySymbol name

            if familySymbol.Name == "Break Line":
                familySymbolToFind = familySymbol
            elif familySymbol.Family.Name == "BNI_BREAKLINE":
                familySymbolToFind = familySymbol

uidoc.PromptForFamilyInstancePlacement(familySymbolToFind)

0 Likes
Message 12 of 19

stever66
Advisor
Advisor

If you got the family to load, the transaction must be working.

 

the sequence is this:

Create a transaction

t.start()

load the family

t.commit()

promptforfamilyplacement

 

see my post above.  The prompt for family placement automatically makes its own transaction so it can't be placed inside any other transaction.

0 Likes
Message 13 of 19

j-kirkpatrick
Enthusiast
Enthusiast

The problem I am having is that it doesn't know when to end the while statement due to it wanting to start a new transaction( I believe..)

 

Exception : Autodesk.Revit.Exceptions.InvalidOperationException: Starting a new transaction is not permitted. It could be because another transaction already started and has not been completed yet, or the document is in a state in which it cannot start a new transaction (e.g. during failure handling or a read-only mode, which could be either permanent or temporary).


 This is the error I'm getting.

 

 Now when I remove the Load family sequence I get this error.

 

Exception : System.MissingMemberException: Name

 

0 Likes
Message 14 of 19

j-kirkpatrick
Enthusiast
Enthusiast

The problem I am having is that it doesn't know when to end the while statement due to it wanting to start a new transaction( I believe..)

 

Exception : Autodesk.Revit.Exceptions.InvalidOperationException: Starting a new transaction is not permitted. It could be because another transaction already started and has not been completed yet, or the document is in a state in which it cannot start a new transaction (e.g. during failure handling or a read-only mode, which could be either permanent or temporary).


 This is the error I'm getting.

 

 Now when I remove the Load family sequence I get this error.

 

Exception : System.MissingMemberException: Name

 

0 Likes
Message 15 of 19

stever66
Advisor
Advisor

I can't tell from your code where you had the transactions.  I don't see any commit statements.

 

Can you post the code exactly as you were trying to get it to work?

0 Likes
Message 16 of 19

j-kirkpatrick
Enthusiast
Enthusiast

This is what I have that is currently giving that error. If you run it once it'll load the family then give the name error I posted before, then run it again and it will give the transaction error.

 

 

from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import *
from Autodesk.Revit.DB.Analysis import *
from Autodesk.Revit.UI import *

import clr
# clr.AddReferenceByName("PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
# clr.AddReferenceByName("PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
clr.AddReferenceByPartialName('PresentationCore')
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName('System.Windows.Forms')
import System.Windows

uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document
selection = [ doc.GetElement( elId ) for elId in __revit__.ActiveUIDocument.Selection.GetElementIds() ]

def alert(msg):
    TaskDialog.Show("pyRevit", msg)

def quit():
    __window__.Close()

if len(selection) > 0:
    el = selection[0]




from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

#t = Transaction(doc, "Place Element")
#Autodesk.Revit.DB.Transaction  
#t.Start()

familyCollector = FilteredElementCollector(doc)
familyCollector.OfClass(clr.GetClrType(FamilySymbol))
familySymbolToFind = None
enumerator = familyCollector.GetEnumerator()
while enumerator.MoveNext():
    familySymbol = enumerator.Current

#To search by FamilySymbol name
# TaskDialog.Show("Revit", familySymbol.Name.ToString());

    if familySymbolToFind == None:
        Transaction(doc, "Place Family").Start("Load Family")
    doc.LoadFamilySymbol("C:\Temp\Break Line.rfa", "Break Line")
    TaskDialog.Show("Revit", "family loaded")
    enumerator = familyCollector.GetEnumerator()
    while enumerator.MoveNext():
            familySymbol = enumerator.Current

#To search by FamilySymbol name

            if familySymbol.Name == "Break Line":
                familySymbolToFind = familySymbol
            elif familySymbol.Family.Name == "Break Line":
                familySymbolToFind = familySymbol

uidoc.PromptForFamilyInstancePlacement(familySymbolToFind)

0 Likes
Message 17 of 19

j-kirkpatrick
Enthusiast
Enthusiast

As well I don't know if you're using this or not but this is what I am using.

 

I have this add on that is rather complex using python

https://github.com/eirannejad/pyRevit

 

Then if you want to develop and test your code while in revit you can use this RPS (revit python shell)

https://github.com/architecture-building-systems/revitpythonshell 

 

Eirannejad also has a video of how to easily add your own ribbon and buttons which i've already done.

0 Likes
Message 18 of 19

stever66
Advisor
Advisor

I have no idea. 

 

Its very easy to get this to run in C#. 

 

In python, I have no idea.  There just aren't enough code examples to find.  And in python, I'm constantly having problems with the compiler not liking the formatting.    I keep having to delete returns, and reinsert them just to get the program to build.

 

Then it just keeps telling me the project built with success, but it won't let me run it.  There is an error somewhere, and it won't give me a clue where or why.  

 

I started with python also, but quickly moved to C# due to these same problems.

0 Likes
Message 19 of 19

j-kirkpatrick
Enthusiast
Enthusiast

I'm open for it to be in C#, I'll just have to figure out how to redo the buttons which is no big deal. Is there a simple way to change this from python to C#?

I also have the person who created pyRevit looking into a python solution. So hopefully we'll be the pioneers in python/C# button detail component area.

0 Likes