Import (neither add nor create) Shared Parameters and their Properties into Revit Family with Dynamo

Dear Dynamo Users,

I have got plenty of examples on how to “add” (new) shared parameters to a Revit Family using various custom nodes in Dynamo. All the topics I found are all about “adding” in the sense of creating new parameters into a revit family with a script, building them through a collection of listed properties that come, for example, out of an excel file.
Is there someone that figured out how to actually import into a family shared parameters that are already created with their own GUID and either come from a project where they are already loaded (as project parameters) or from a .txt file?

My situation is that I have families loaded in a project template, where many project parameters are shared parameters and they are assigned to family categories within the project. I need to automate the import of these parameters into these families, keeping all their properties and GUID.

Good question.
The short answer is that Revit always imports a shared parameter from a shared parameter file (or the newer cloud parameter service) So you are always “importing” a shared parameter from an external file.

The basic API is ```
SharedParameterElement Create(Document document, ExternalDefinition sharedParameterDefinition)

Dynamo always has to first create the definition in the parameter file, before it can import it. You can see this if there is no shared parameter file open and missing in Revit. Dynamo will give you an error: “No shared parameter file found.”

Of course, when you initially create a parameter definition, the Guid is automatically created. I find this a little unpredictable with multiple users and the possibility of multiple shared parameter files. So be explicit about your shared parameter files or you can end up creating different shared parameters of the same name with different Guid. Not what you want.

For those reasons, I hard code my shared parameters with preset Guids.

  • I create a new temporary empty shared parameter file.
  • Create my definitions in that file. (Really, I’m just writing my own shared parameter file straight to text.)
  • Switch the users Revit to that file.
  • Import the parameters needed.
  • Switch back to the users shared parameter file.
  • …and delete my temporary shared parameter file.

This way I always get the parameter setup exactly the way it should be and the bindings are set correctly as well.

All that is simpler to do with just python rather than dynamo. One node as opposer to quite a few different nodes for a variety of different tasks. It also allows fast import of all standard shared parameters when creating new families. Just look at the family type and import the relative info based on my dictionary.

You could also look at the cloud parameter service as a resource for centralizing parameters…

1 Like

Hi, I´m working on the same case as Riccardo (same company). The idea is to add Parameter to a family, which is already loaded into the project by selecting it. I created a temporary sharedparameterfile where I get the ExternalDefinition.

Document familyDoc = doc.EditFamily(fam);

using (Transaction t = new Transaction(familyDoc, "Add ParamtersToFamily"))
{
    t.Start();

    foreach (db_Parameter param in parameters)
    {
        ExternalDefinition definition = ThisApplication_ParameterManager.GetParameterFromGG("MySharedParameter", ggFile);
        FamilyManager familyManager = familyDoc.FamilyManager;
        FamilyParameter existingParam = familyManager.get_Parameter("MySharedParameter");

        if (existingParam == null)
        {
            try
            {
                int index = 32;
                var builtingroups = ParameterUtils.GetAllBuiltInGroups().ToList();
                var group = LabelUtils.GetLabelForGroup(builtingroups[index]);
familyCategory, bool isInstance);

                familyManager.AddParameter(definition, builtingroups[index], true);
            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show($"Fehler B6E3F1D0 - {ex.Message}");
                throw;
            }
            
        }
    }

    t.Commit();
}

Revit just returns “Shared parameter creation failed” without telling any reason for that… Does anyone know what to do?

Which Revit version?
The above method is now obsolete. You should be using ForgeTypeId’s.

Revit 23. I´m using the ForgetypeId for Text.

I also tried it in RVT 21 wird BuiltInParameterGroup.PG_TEXT, but it did not work either.

Seems you have an unnecessarily complicated workflow…

I might be missing something (I usually do), but I recall that if you add the shared parameter to the project all of the families should get the parameter added instantly - so when you open the family from the project the parameters should already be added… right?

Is it different for templates?

That’s correct. Often, but not always desirable.

You may have many different families as subcategories under let’s say - generic. Because Revit doesn’t have a category for absolutely everything and that’s the category we abuse. Or you want chair and tables to be different under furniture.

So, let’s say you want seat material as a shared parameter for chairs. But tables don’t have a seat material. If you do the parameter at the family level, they will ignore each other’s unique parameters but still share the ones they have in common. A useful feature if used wisely. Or it can confuse the **** out of people.

At any future point in time, you can add that same parameter at the project level, and it will then push down into all the families of that category.

It would be nice to have that option extended to subcategories. But I’m dreaming.

And I forgot to mention…
You can do some silly things with a tag that has labels for each of the unique parameters. They will pick up the unique parameters from the tagged family. But ignore the parameters that aren’t present. Get creative.

1 Like

Gotcha. I didn’t see ‘sunset of families’ as it indicated the parameter was already in the project, but my thought process would still be valid for a partial loading.

So likely the fastest manual path will be to generate a new .rvt from no template, add the shared parameters desired for the subset of families to the project, load the desired families into the temp project, save out the family library (thereby getting a detached copy with the parameters on disc somewhere so that you can add any of them back without needing to reattach the parameter should they get purged), and then load them into the template (overwriting any existing version and it’s parameter values) and save the template.

Yea you could tell Dynamo to open each family document, add the parameter, save the family to disc, reload the family into the project, and close the family document… but before you do ask if you would recoup the time spent building that automation - it’s likely a net loss unless this is something you have to do often (and I would take a long hard look in the mirror if it was something I was doing often).

In any case I am a bit off topic…

@leisure and @Riccardo.Rocchi I am seeing both C# and Python code as well as mention of using custom nodes, and this isn’t a good fit for ‘mix and match’ tools. How are you planning on developing this?

First of all thank you and @aaron_rumple for your answers.
I am currently testing this both with Revit 21 and 23. What you recall is sadly not the case, that´s exactly why we are here. It doesn´t seem to work with opening the family from neither a template nor for a project in my case. The families only keeps their original family parameters and shared parameters that were created within the family, regardless if new ones were added to them within the .rte or .rvt file as project parameters. I can see why revit was set to be this way, because it might not be desirable, but in our case it would make things simple such as using a project or a template as a way to manage and set up a whole library of families with the right type parameters.

So… Revit should not be acting differently for you. Can you post a sample 2023 project with one family instance and a shared parameter where opening the family in the family editor shows no shared parameter on it? I’ll need to see exactly what is happening to get to the root cause.

The idea was to create a prototype in Dynamo and use C# afterwards for a plugin. When Dynamo did not work, I tried to get it into C# straight away.

Ok - post the model I asked for above and I’ll see about finding a fix for that as well as looking into the code issue.

I cannot upload anything, because I´m a new user. I put it one Google Drive.

I don’t have a public google account so you’ll need to make that fully accessible without a sign in or use another tool. Gimme 3 min and I’ll bump you both to the next trust level enabling uploads.

Edit: It’s an open link now - file received.
Edit 2: Trust levels bumped. Should be able to post directly in the next minute or two.

1 Like

Ok… this is weird. I don’t recall this behavior and it seems like it shouldn’t be based on the help docs, but in any case…

Lemme look into some Python.

Typically it’s best to edit the .rfa in your library directly and then load them in, but editing from the project also can work if you save out the library when done. Promise to do the later for me after you execute this?

THis shoudl get you started @leisure and @Riccardo.Rocchi:

Add Shared parameters via Iron Python
########################################
############## Properties ##############
########################################
__author__ = 'Jacob Small'
__version__ = '0.1.0'
__description__ = "Attempts to add shared parameters to selected families when given lists of parameter names, parameter group types, and instance booleans of matching length, allowing the user to send a report of the results to the clipboard when done."
__RevitBuilds__ = "2023.1"
__DynamoBuilds__ = "2.16"
__ReleaseNotes__ = "POC only - not completely tested. Test thoroughly before using in production."
__Dependencies__ = "IronPython 3 engine compatible with your Dynamo environment - see the DynamoIronPython3 package details for more info."
__Copyright__ = "2025, Autodesk Inc."
__License__ = "Apache 2"



########################################
### Configure the Python environment ###
########################################
### standard python imports ###
import sys #add the sys class to the Python environment so we can work with the sys objects
import clr #add the CLR (common language runtime) class to the Python environment so we can work with .net libraries
import uuid #add the UUID so we can generate random GUIDs for the family load options interface
import System #import the system module to the Python environment 
import subprocess #import the subprocess module to the Python environment so we can access the clipboard
### Dynamo Revit imports ###
clr.AddReference("RevitNodes") #add Dynamo's Revit nodes library to the clr
import Revit #import Dynamo's Revit node class
clr.ImportExtensions(Revit.Elements) #add the element conversion methods to the CLR
clr.ImportExtensions(Revit.GeometryConversion) #add the geometry conversion methods to the CLR
clr.AddReference("RevitServices") #add the Revit services library to the CLR
import RevitServices #import the Revit services class to the Python environment
from RevitServices.Persistence import DocumentManager #import the document manager class to the Python environment 
from RevitServices.Transactions import TransactionManager #import the transaction manager class to the Python environment 
### Revit API imports ###
clr.AddReference("RevitAPI") #add the Revit API to the CLR
import Autodesk #add the Autodesk class to the Python environment 
from Autodesk.Revit.DB import * #import every class of the Revit API to the Python environment
clr.AddReference('RevitAPIUI') #add the Revit UI api to the CLR
from Autodesk.Revit.UI import TaskDialog, TaskDialogCommonButtons, TaskDialogResult, TaskDialogCommandLinkId #import the the required classes of the Revit UI for popup reporting
clr.AddReference("System.Reflection") #add system reflection to the CLR
from System.Reflection import BindingFlags #import binding flags to the Python environment

#########################################
######## Classes and Definitions ########
#########################################
class FamilyLoadOptionsInterface(IFamilyLoadOptions): #class for managing the family load options
    __namespace__ = str(uuid.uuid4())#defines a random GUID for the namespace.. this helps prevent issues on occasion but is not always required
    def OnFamilyFound(self, familyInUse, overwriteParameterValues): #defines the action to take when a family is found
        overwriteParameterValues = True #sets the overwrite parameter values to true
        return True #returns true
    def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues): #defines the action to take when a shared family is found
        overwriteParameterValues = True #sets the overwrite parameter values to true
        return True #returns true
def copy2clip(report): #send the report to the clipboard
    cmd='echo '+report+'|clip' #define the command to copy to the clipboard
    return subprocess.check_call(cmd, shell=True) #call the command to copy to the clipboard


#########################################
###### Global variables and inputs ######
#########################################
### documents and standard variables ###
doc = DocumentManager.Instance.CurrentDBDocument #the current Revit document
app = DocumentManager.Instance.CurrentUIApplication.Application  #the current application 
TransactionManager.ForceCloseTransaction(TransactionManager.Instance) #forces any active Revit transaction to close

### imports, output, and unwrapping ###
reportData = [] #defines the output as a list
run = IN[0] #sets the 'run' variable to the contents of IN[0] from the Dynamo environment
if not run: #if run isn't true
    reportData.append("\r\rSet run to true before executing.\r\r") #set the report data accordingly
families = UnwrapElement(IN[1])#import the families from IN[1] of the Dynamo environment and convert to native Revit families
if not families.__class__ == list: families = [families] #ensure that families is a list, not an individual object, so that we can always prepare for a loop
definitionNames = IN[2] #get the definition names from IN[2] of the Dynamo environment
if not definitionNames.__class__ == list: definitionNames = [definitionNames] #ensure that definitionNamesis a list, not an individual object, so we can always prepare for a loop
groupTypes = UnwrapElement(IN[3]) #get the group types from IN[3] of the Dynamo environment
if not groupTypes.__class__ == list: groupTypes = [groupTypes] #ensure that group types is a list, not an individual object, so we can always prepare for a loop
instanceParameterBool = IN[4] #get the instanceParameterBool from IN[4] of the Dynamo environment
if not instanceParameterBool.__class__ == list: instanceParameterBool = [instanceParameterBool] #ensure that instapceParameterBool is a list, no an individual object, so we can alwasy prepare for a loop
if len(set([len(groupTypes), len(definitionNames), len(instanceParameterBool)]))!=1: #check of the length of group types, definition names, and instanceParameterBool are not equal
    reportData.append("The length of definition names, group types and instance booleans are not the same. Ensure even list lengths and try again.") #if so append a message to the report
    run = False #set run to false


#########################################
############ Code goes here #############
#########################################
#Gather the external definitions to attach. I'm using a name filter here with error handling for names which don't exist. You likely want to be a bit more robust here.
sharedParameterFile = app.SharedParametersFilename #get the shared parameter file
externalDefinitionFile = app.OpenSharedParameterFile() #open the shared parameter file
externalDefinitionGroups = [i for i in externalDefinitionFile.Groups] #get the definition groups from the shared parameter file 
externalDefinitions = [j for i in externalDefinitionGroups for j in i.Definitions ] #get the definitions from each definition group 
missingNames = [i for i in definitionNames if not i in [j.Name for j in externalDefinitions] ] #get any parameter names which were given but do not appear in the definitions
if missingNames: #if there are missing names
    missingNames = "\r   "+"\r   ".join(missingNames) #format the list of missing names a string
    reportData.append("\r\rNot all definition names could be found in your shared parameter file:\r      {0}\r\r Confirm you're spelling things right and try again. The following parameter names were not found: \r{1}\r\r\r".format(sharedParameterFile,missingNames)) #append the missing names message to the report data
    run = False #set run to false
loadOptions = FamilyLoadOptionsInterface() #build a IFamilyLoadOptions object using our FamilyLoadOptionsInterface class

# Modify the family documents and reload them
if run: #if run is still true
    for family in families: #for each family 
        results = ["Attempting to add shared parameters to {}.".format(family.Name)] #start the list of result strings
        familyDocument = doc.EditFamily(family) #open the family document
        transaction = Transaction(familyDocument,"Add parameters") #craete a transaction in the family document
        transaction.Start() #start the transaction 
        familyManager = familyDocument.FamilyManager #get the family manager
        params = familyManager.Parameters #get the parameters in the family manager
        sharedParamGUIDs = [param.GUID for param in params if param.IsShared ] #get the GUID of all shared parameters
        for externalDefinition, groupType, instanceBool in zip(externalDefinitions, groupTypes,  instanceParameterBool): #for each external definition, group type, and instance boolean
            if not externalDefinition.GUID in sharedParamGUIDs: #if the external definition's GUID is not in the shared parameter GUIDs
                familymanager.AddParameter(externalDefinition, ForgeTypeId(groupType[0].TypeId), instanceBool) #add the shared parameter
                results.append("Shared parameter {} added".format(externalDefinition.Name)) #append the success message to the result
            else: #otherwise
                results.append("Shared parameter {} already existed.".format(externalDefinition.Name)) #append a string indicating the parameter already existed
        transaction.Commit() #commit the transaction
        familyDocument.LoadFamily(doc, loadOptions) #load the family into the the document
        results.append("{} Reloaded.".format(family.Name)) #append the loaded message to the result strings
        familyDocument.Close(False) #close the family document
        reportData.append("   "+"\r      ".join(results)) #append the results to the report data


#########################################
##### Return the results to Dynamo ######
#########################################
#format the OUT value
report = "Parameter attchment report:\r"+"\r".join(reportData) #format the report as a string

#trigger a task dialog for copying the report to the clipboard
dialog = TaskDialog("Parameter Attachment Results") #create a task dialog
dialog.MainInstruction = "Attempt at attaching parameters complete." #set the main instruction of the task dialog
dialog.MainContent = "Send report to clipboard?" #set the main content of the task dialog
dialog.CommonButtons = TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No #add the Yes and No buttons
dialogResult = clr.GetClrType(dialog.GetType()).InvokeMember("Show", BindingFlags.InvokeMethod , None, dialog, []) #trigger the dialog via reflection and get the result back to Dynamo

#if the task dialog result was Yes, send the result to the clipboard
if dialogResult == TaskDialogResult.Yes: #check if the task dialog result was 'Yes'
    copy2clip(report) #send the text to the clipboard
OUT = report #return the report to the Dynamo environment

Annotated it to the nth degree so hopefully you can work out the logic should you move over to C#. Requires the IronPython3 engine (IronPython2 might also work but I don’t recommend it due to the security risk).

1 Like

Were the bindings set correctly? If bindings aren’t set, you will get the parameter, but it won’t be attached to anything.
You should be able to do that manually and see the result.
Any parameter added at the project level will get pushed down into all families of that class when they are added to the project - regardless of if the parameter existed at the family level or not.

Hi Jacob,
thank you :smiley: I will look into this at the beginng of the next week and report back if it worked :smiley:

1 Like

Hi Jacob,
I got it to work now. Thanks for the quick help :wink: I had a problem mit my ExternalDefinition. I changed the GG-Path before running the script to another file (so that projects can keep their current GG-File) and changed it back straight away, bevore adding it to the families. This works for “normal” parameters, but obviously it does not work with families. Now I´m changing it before adding the parameters to the family, and change it back after saving and reloading the family into the project.

This is my working C#-Code now (if someone is interested) :smiley:

                var encoding = new UTF8Encoding(false); 
                String currentPath = doc.Application.SharedParametersFilename;
                doc.Application.SharedParametersFilename = filepath;
                DefinitionFile defFile = null;
                defFile = doc.Application.OpenSharedParameterFile();

                Document familyDoc = doc.EditFamily(fam);
                var builtingroups = ParameterUtils.GetAllBuiltInGroups().ToList();

                using (Transaction t = new Transaction(familyDoc, "Add Parameters"))
                {
                    t.Start();
                    FamilyManager familyManager = familyDoc.FamilyManager;
                    var famParams = familyManager.Parameters;

                    foreach (db_Parameter param in parameters)
                    {
                        ExternalDefinition famDef = ThisApplication_ParameterManager.GetParameterFromGG(param.RVT_Name, defFile);

                        FamilyParameter existingParam = familyManager.get_Parameter(param.RVT_Name);

                        if (existingParam == null)
                        {
                            try
                            {
#if R2023
                                ///ForgeTypeId forgeTypeId = ForgeTypeId.
                                var yzx = SpecUtils.GetAllSpecs();
                                //var group = LabelUtils.GetLabelForGroup(yzx[70]);
                                //LabelUtils.GetLabelForSpec(ForgeTypeId)

                                int index = 32; // TEXT
                                
                                List<string> groups = new List<string>();
                                foreach (var g in builtingroups)
                                {
                                    groups.Add(LabelUtils.GetLabelForGroup(g));
                                }

                                var group = LabelUtils.GetLabelForGroup(builtingroups[index]);

                                //ForgeTypeId id = builtingroups[index];

                                ForgeTypeId textGroup = builtingroups[index];
                                
                                Category cat = familyDoc.Settings.Categories.get_Item(BuiltInCategory.OST_StructuralColumns);
                                
                                familyManager.AddParameter(famDef, textGroup, false);

#else
                            System.Windows.MessageBox.Show("Muss noch eingerichtet werden für Revit 2021");
#endif
                                //familyManager.AddParameter(definition,BuiltInParameterGroup.PG_TEXT,false);
                            }
                            catch (Exception ex)
                            {
                                System.Windows.MessageBox.Show($"Fehler B6E3F1D0 - {ex.Message}");
                                throw;
                            }
                        }
                    }
                    t.Commit();
                }

                var loadOptions = new FamilyLoadOptionsInterface();

                familyDoc.LoadFamily(doc, loadOptions);
                familyDoc.Close(false);

                doc.Application.SharedParametersFilename = currentPath;
public class FamilyLoadOptionsInterface : IFamilyLoadOptions
{
    public bool OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
    {
        overwriteParameterValues = true; // Sets the overwrite parameter values to true
        return true; // Returns true
    }

    public bool OnSharedFamilyFound(
        Family sharedFamily,
        bool familyInUse,
        out FamilySource source,
        out bool overwriteParameterValues)
    {
        overwriteParameterValues = true; // Sets the overwrite parameter values to true
        source = FamilySource.Project;
        return true; // Returns true
    }
}
2 Likes