I am trying to get a list of the materials that are contained within each wall type. Ideally I would like the names but as a start I am just trying to get the material IDs. I am using the following code (adapted from the Revit Intro Lab 1) where I iterate through the wall types and have a sub-routine to iterate through the materialIds of each WallType. Using this code it seems that the sub-foreach routine is not working and the string p that I create is left unfilled. Any advice would be much appreciated.
FilteredElementCollector wallTypes // 2014 = new FilteredElementCollector(rvtDoc) .OfClass(typeof(WallType)); string s = ""; foreach (WallType wallType in wallTypes) {
string p = ""; foreach (ElementId mat3 in wallType.GetMaterialIds(false)) { p += mat3.ToString() + "\r\n"; } s += wallType.Name + p + "\r\n"; } // Show the result: TaskDialog.Show( "Revit Intro Lab", "Wall Types (in main instruction):\n\n" + s);
Solved! Go to Solution.
Solved by GonçaloFeio1321. Go to Solution.
Try this:
public IEnumerable<Material> Get(WallType wallType) {
if (wallType== null)
{
throw new ArgumentNullException(nameof(wallType));
}
var document = wallType.Document; var layers = wallType.GetCompoundStructure().GetLayers(); foreach (var layer in layers) { var materialId = layer.MaterialId; if (materialId != ElementId.InvalidElementId) { yield return document.GetElement(layer.MaterialId) as Material; } } }
The Element GetMaterialIds method works only for model instances, not their types.
Edit: Added a nice check for null
Thanks for your reply. I have tried implementing your Get() method as follows:
FilteredElementCollector wallTypes // 2014 = new FilteredElementCollector(rvtDoc) .OfClass(typeof(WallType)); string s = ""; foreach (WallType wallType in wallTypes) { s += Get(wallType) + "\r\n"; } TaskDialog.Show( "Revit Intro Lab", "Wall Types (in main instruction):\n\n" + s);
However when I print the result to dialog I get the following message:
"Material_Select_2 + <Get>d__0" for each instance
Material_Select_2 is the name of my class, I am guessing this has something to do with the variable it is stored in, do you have any idea how I can convert this to a string?
Thanks
The Get method returns a IEnumerable<Material>, even duplicates. And you are just adding that to the string.
Try replacing the line s += Get(wallType) + "\r\n"; wih this:
foreach (var material in Get(wallType)) { builder.AppendLine($"Material: {material.Name}"); }
Thanks again for your reply. I incorporated this method and now the code is generating this error " System.NullReferenceException: Object reference not set to an instance of an object . at Material_Select_2.<Get>d__0.MoveNext()"
I tried to incorporate an exception, as you can see below, but I am still getting the same error:
StringBuilder builder = new StringBuilder(); string s = ""; foreach (WallType wallType in wallTypes) { foreach (var material in Get(wallType)) { if (material == null) { throw new ArgumentNullException(nameof(material)); } builder.AppendLine($"Material: {material.Name}"); } }
Allan, here is the refactored method.
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { var uiDoc = commandData.Application.ActiveUIDocument; var rvtDoc = uiDoc.Document; using (var collector = new FilteredElementCollector(rvtDoc)) { using (var filtered = collector.OfClass(typeof(WallType))) { var builder = new StringBuilder(); foreach (var wallType in filtered.OfType<WallType>()) { if (wallType.Kind == WallKind.Basic) { builder.AppendLine($"WallType: {wallType.Name}"); foreach (var material in Get(wallType)) { builder.AppendLine($"Material: {material.Name}"); } } } TaskDialog.Show("Output", builder.ToString()); } } return Result.Succeeded; // Why Failed? }
Can't find what you're looking for? Ask the community or share your knowledge.