From Dynamo to C# – Get and Set Parameters

I’m the kind of guy that uses Dynamo for practical things, an indispensable swiss army knife that I used to manipulate data.

Getting and setting parameters is obviously key. This time around we look at how this is done in Dynamo and then how we can achieve that same thing with code.

In this example I will look at how to build up an asset code based on the level name and the mark parameter, combining those into a single new parameter value.

We will start with the code from the previous Dynamo to C# post where we collected all the AHU families in the model. For convenience, here is the complete code from the previous post:

public void getMechanicalEquipment()
{
	//get current document
	Document doc = this.ActiveUIDocument.Document;
	
	//setup collector and filter
	var collector = new FilteredElementCollector(doc);
	var filter = new ElementCategoryFilter(BuiltInCategory.OST_MechanicalEquipment);

	//collect all mechanical equipment
	var allMechanicalEquipment = collector.WherePasses(filter).WhereElementIsNotElementType().Cast<FamilyInstance>();

	//use LINQ to filter for familyInstances containing AHU in the family name
	var ahuEquipment = from fi in allMechanicalEquipment
		where fi.Symbol.FamilyName.ToUpper().Contains("AHU")
		select fi;

	//setup string builder to store data
	StringBuilder displayInfo = new StringBuilder();

	//loop through, collect data
	foreach (FamilyInstance fi in ahuEquipment)
		{
			var elementinfo = string.Format("Family Name: {0} Element Id: {1}", fi.Name, fi.Id);
			displayInfo.AppendLine(elementinfo);
		}

	//display results
	TaskDialog.Show("Watch", displayInfo.ToString());
}

Getting the Level

To do this in Dynamo, you would need to string together the following nodes:

In Dynamo we need to use the Level.Name node to get the actual name. The reason for this is that when we feed Level into the Element.GetParameterByName node, the output is actually an element id. Feeding that element id into the Level.Name node returns the name without any further work required.

So how do we replicate this in C#?

First, we need to focus on the foreach loop, as this is where the work is done for each element that we have selected. We have already done the groundwork to select our elements, but how do we find out what level each element is associated with?

We need to use the level class from the API, this allows us to retrieve the name of the level as you would see it when inspecting the element you’re working with.

Using the level class, we create a variable theLevel which we find from the family instance fi.

Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;

This line is doing half the work of our Level.Name Dynamo node. It is taking the element id of the level that is reported from the Level parameter of the selected AHU family, then selecting the level element.

From there we can simply use theLevel.Name to display the level name.

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
{
	Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;
	var elementinfo = string.Format("Family Name: {0} Level: {1}", fi.Name, theLevel.Name);
	displayInfo.AppendLine(elementinfo);
}

Note that instead of using the elementinfo variable to display the family name element id of the AHU in the dialogue box as per the previous example, we are now displaying the family name and the associated level name.

Getting the Mark

There isn’t a whole lot of change between the process for picking up the mark parameter in Dynamo compared to the level name; the only real change is that we can drop the Level.Name node.

This is because when we tell the Element.GetParameterValueByName node that we want a parameter named Mark, it returns the result as a string.

In C# we want to use get_Parameter on the family instance fi.

But before we get too far, we need to understand that built in parameters are handled slightly differently in Revit to user defined parameters. Built in parameters are contained within an enumerated list, you can review the list of built in parameters on Revit API Docs.

In the API, the mark parameter is stored as ALL_MODEL_MARK

Retrieving built in parameters this way is more reliable as there is no risk that you will pick up a user defined parameter of the same name.

Now that we know this, our equivalent line of code to read the mark parameter in C# looks like this:

Parameter theMark = fi.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);

But we still don’t have the parameter value. To retrieve the parameter value, we need to use the AsString method on our paramter theMark which will return the parameter value as a string.

It is important to use the correct method to avoid any potential errors returning the data, for numbers use AsDouble, for integers use AsInteger and element ids use AsElementId.

Our foreach loop should now look like so:

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
{
	//get the level
	Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;
	//get the mark
	Parameter theMark = fi.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);

	var elementinfo = string.Format("Family Name: {0} Level: {1}", fi.Name, theLevel.Name);
	displayInfo.AppendLine(elementinfo);
}

Again, adding this to our task dialogue, we end up with the following results.

Building the New String

We can build our new parameter value a few different ways in Dynamo, either by using the String.Concat node, or by using a code block which is my preferred method.

There are also a few different way you can approach building the new string in C# as well. The simplest way is to approach it the same way as we do in the Dynamo code block.

First you define the string, in this example we are naming it newValue and we take our level name theLevel.Name, add the string _AHU_ and the we take our mark value with theMark.AsString().

string newValue = theLevel.Name + "_AHU_" + theMark.AsString();

The other method is to use a string builder which we have been using in our examples to create our dialogue box message. The string builder version would look like so:

var newValue = string.Format("{0}_AHU_{1}", theLevel.Name, theMark.AsString());

Either method is acceptable, but if you’re building more complex strings the string builder is the recommended way to go. Both options give the exact same results:

Setting the New Parameter

In Dynamo, the job here is done by the trusty Element.SetParameterByName node seen above, but in C# there is a tiny bit more to do.

The first thing we need to do to set out parameter is define which parameter we want to set. In this example I’m going to use the built in parameter Comments which is stored in Revit as
ALL_MODEL_INSTANCE_COMMENTS.

Parameter theComments = fi.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);

The next step is to start a transaction. Every time you want to change something in a Revit file via the API, you need to make the changes inside a transaction. If you have ever looked into Dynamo nodes containing Python code, you would have seen something similar to this before.

//set the parameter
using(Transaction t = new Transaction(doc, "Set Parameter"))
{
	t.Start();
	//do stuff here
	t.Commit();
}

We define a new transaction t which is being performed in the current document doc which will appear in the undo/redo list as Set Parameter.

Inside our transaction, we set the parameter by using the Set method on our parameter theComments.

//set the parameter
using(Transaction t = new Transaction(doc, "Set Parameter"))
{
	t.Start();
	
	theComments.Set(newValue);
	
	t.Commit();
}

And that’s it! We have successfully pulled information from multiple parameters, generated new string data and set another parameter using C#.

The complete code is below:

public void getsetParameters()
{
//get current document
Document doc = this.ActiveUIDocument.Document;

//setup collector and filter
var collector = new FilteredElementCollector(doc);
var filter = new ElementCategoryFilter(BuiltInCategory.OST_MechanicalEquipment);

//collect all mechanical equipment
var allMechanicalEquipment = collector.WherePasses(filter).WhereElementIsNotElementType().Cast<FamilyInstance>();

//use LINQ to filter for familyInstances containing AHU in the family name
var ahuEquipment = from fi in allMechanicalEquipment
	where fi.Symbol.FamilyName.ToUpper().Contains("AHU")
	select fi;

//setup string builder to store data
StringBuilder displayInfo = new StringBuilder();

//loop through, collect data
foreach (FamilyInstance fi in ahuEquipment)
	{

		//get the level
		Level theLevel = fi.Document.GetElement(fi.LevelId) as Level;

		//get the mark
		Parameter theMark = fi.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);

		//get the comments
		Parameter theComments = fi.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS);

		var newValue = string.Format("{0}_AHU_{1}", theLevel.Name, theMark.AsString());

		var elementinfo = string.Format(newValue);
		displayInfo.AppendLine(elementinfo);

		//set the parameter
		using(Transaction t = new Transaction(doc, "Set Parameter"))
		{
			t.Start();

			theComments.Set(newValue.ToString());

			t.Commit();
		}

	}

}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: