Revit family parameter (FamilyParameter) shares a lot of properties such as BuiltInParameterGroup, ParameterType, StorageType, and DisplayUnitType with the model parameter and has some unique ones as well such as IsReporting, IsInstance and CanAssignFormula as we discussed previously.
Another very special character of the FamilyParameter is that it does not store a value itself. Instead, its value has to be retrieved from each FamilyType instance individually. It behaves very different from Parameter in this regard. For example, each element has a Property collection and each Property carries a value, but the FamilyParameter collection has a single copy in the FamilyManager object of each Family Document and each FamilyType can have a different value for the same FamilyParameter.
So we have to find a FamilyParameter from somewhere, the FamilyManager.Parameters property, and retrieve its value from somewhere else, through the methods AsInteger(), AsDouble(), AsString(), and AsElementId() of FamilyType. In this post, let’s see how to retrieve values of all FamilyParameter instances regarding all available FamilyType objects in a family document, organize the values and any associated information in a good store center, and make it ready to use. The following help method stores the FamilyParameter name and value of all FamilyParameter instances and the name of each associated FamilyType into a dictionary which has nested lists as entries: public static Dictionary<string, List<KeyValuePair<string, object>>> NameToTypeValuesMap(FamilyManager famMan)
{
Dictionary<string, List<KeyValuePair<string, object>>> fpNameToTypeValuesMap
= new Dictionary<string, List<KeyValuePair<string, object>>>(); foreach (FamilyParameter p in famMan.Parameters)
{
List<KeyValuePair<string, object>> typeValuePairs = new List<KeyValuePair<string, object>>();
foreach (FamilyType type in famMan.Types)
{
switch (p.StorageType)
{
case StorageType.Double:
typeValuePairs.Add(new KeyValuePair<string, object>(type.Name, type.AsDouble(p)));
break;
case StorageType.Integer:
typeValuePairs.Add(new KeyValuePair<string, object>(type.Name, type.AsInteger(p)));
break;
case StorageType.String:
typeValuePairs.Add(new KeyValuePair<string, object>(type.Name, type.AsString(p)));
break;
case StorageType.ElementId:
typeValuePairs.Add(new KeyValuePair<string, object>(type.Name, type.AsElementId(p)));
break;
case StorageType.None:
typeValuePairs.Add(new KeyValuePair<string, object>(type.Name, "N/A"));
break;
default:
typeValuePairs.Add(new KeyValuePair<string, object>(type.Name, null));
break;
}
} fpNameToTypeValuesMap.Add(p.Definition.Name, typeValuePairs);
} return fpNameToTypeValuesMap;
} And the following code will convert the informative dictionary into a single CSV string:
public string FamilyParameterValuesInfoToCSVString(
Dictionary<string, List<KeyValuePair<string, object>>> values, out string title)
{
title = "Parameter Name,Family Type,Parameter Value";
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, List<KeyValuePair<string, object>>> kvp in values)
{
sb.Append(kvp.Key + ",," + Environment.NewLine);
foreach (KeyValuePair<string, object> pair in kvp.Value)
{
sb.Append(","+pair.Key + ",");
sb.Append((pair.Value == null ? "null" : pair.Value.ToString()) + Environment.NewLine);
}
}
return sb.ToString();
} Then we can write all the FamilyParameter value information of an opened family document to a CSV file.
…
if (CachedDoc.IsFamilyDocument)
{
Dictionary<string, List<KeyValuePair<string, object>>> fpNameToTypeValuesMap
= NameToTypeValuesMap(CachedDoc.FamilyManager);
using (StreamWriter sw = new StreamWriter(@"c:\FamilyParameterValuesInfo.csv"))
{
string title;
string rows = FamilyParameterValuesInfoToCSVString(fpNameToTypeValuesMap, out title);
sw.WriteLine(title);
sw.Write(rows);
}
}
…
Finally the CSV file can be read into a spreadsheet of Excel. Parameter Name Family Type Parameter Value
Leg Height
60" x 30" Student 0.5
72" x 36" 0.5
60" x 30" 0.5
Depth
60" x 30" Student 2.5
72" x 36" 3
60" x 30" 2.5
Model
60" x 30" Student null
72" x 36" null
60" x 30" null
Top Material
60" x 30" Student 7590
72" x 36" 7590
60" x 30" 7590
Manufacturer
60" x 30" Student null
72" x 36" null
60" x 30" null
Handle/Leg Material
60" x 30" Student 8052
72" x 36" 8052
60" x 30" 8052
Body Material
60" x 30" Student 7589
72" x 36" 7589
60" x 30" 7589
Description
60" x 30" Student null
72" x 36" null
60" x 30" null
Cost
60" x 30" Student null
72" x 36" null
60" x 30" null
Width
60" x 30" Student 5
72" x 36" 6
60" x 30" 5
Height
60" x 30" Student 2.5
72" x 36" 2.5
60" x 30" 2.5
Keynote
60" x 30" Student null
72" x 36" null
60" x 30" null
Assembly Code
60" x 30" Student E2020200
72" x 36" E2020200
60" x 30" E2020200
URL
60" x 30" Student null
72" x 36" null
60" x 30" null
Type Comments
60" x 30" Student null
72" x 36" null
60" x 30" null
From the output, we can notice something that is not so straightforward:
- The value of some string StorageType FamilyParameter instances may be null instead of empty. So please keep this in mind as accessing properties like Length or calling methods like Replace() from/on the string object will throw out a null-object-reference exception in this case.
- Apparently the ToString() has been implemented in the ElementId class as converting the id integer value to a string and return it. So no need to explicitly do the conversion like ElementId.IntegerValue.ToString().
The informative indexed lists can be used for any other purposes as well for sure.
|