Number windows in Revit in a practical way

, , ,
BIMicon Toolbar number windows

As a starting point for our Revit plugin, we converted a macro to number doors into part of our ribbon: A comprehensive guide to creating a customized Revit tab in C#

As a demonstration of the versatility and how modular is the Revit API, we are adapting the macro to number doors after rooms to be able to number windows as well.

Improvements over the previous code

As input came in from users, there are two main elements we have improved:

  • Design Option: The code takes into account doors and windows from different design options, numbering only the design option selected.
  • Phase: The same goes for phases; doors and windows in different phases are numbered after the room to which they open in the phase.

Refactor code to include windows

As a first consideration, windows and doors are FamilyInstance, both having the FromRoom and ToRoom properties. In our initial code, we loop through the collector to retrieve these properties. 

Second, we are going to add some filters in the collector to retrieve elements in our selected phase and design option.

Therefore we only need to refactor the collector to be able to select windows as well, so we can call the method to retrieve windows. After that, we can move all the content to a Helper class and call it independently in a Number Doors and Number Windows Command. Here is what the code looks like:

public static void numberFamilyInstance(Document doc,
                                                BuiltInCategory builtInCategory)
        {
            // Create dictionary to store window-room values
            Dictionary<FamilyInstance, string> instanceNumbers = new Dictionary<FamilyInstance, string>();
            // Create dictionary to store number of windows in room
            Dictionary<string, int> instancesInRoomCount = new Dictionary<string, int>();

            // Design option filter
            ElementDesignOptionFilter designOptionFilter = new ElementDesignOptionFilter(ElementId.InvalidElementId);

            // Select phase
            PhaseArray aPhase = doc.Phases;
            Phase phase = aPhase.get_Item(aPhase.Size - 1);

            ElementId idPhase = phase.Id;
            ParameterValueProvider provider = new ParameterValueProvider(new ElementId((int)BuiltInParameter.PHASE_CREATED));
            FilterNumericRuleEvaluator evaluator = new FilterNumericEquals();
            FilterElementIdRule rule = new FilterElementIdRule(provider, evaluator, idPhase);
            ElementParameterFilter paraFilter = new ElementParameterFilter(rule);

            // Collect all windows in project
            using (FilteredElementCollector instances = new FilteredElementCollector(doc).OfCategory(builtInCategory)
                                            .WhereElementIsNotElementType().WherePasses(designOptionFilter).WherePasses(paraFilter))
            {
                // Retrieve rooms from windows
                foreach (FamilyInstance inst in instances)
                {
                    if (inst.ToRoom != null)
                    {
                        string roomNumber = inst.ToRoom.Number;
                        Helpers.InstanceFromToRoom(instancesInRoomCount, roomNumber, instanceNumbers, inst);
                    }
                    else if (inst.FromRoom != null)
                    {
                        string roomNumber = inst.FromRoom.Number;
                        Helpers.InstanceFromToRoom(instancesInRoomCount, roomNumber, instanceNumbers, inst);
                    }
                }
            }

            // Create transaction and make changes in Revit
            using (Transaction t = new Transaction(doc, "Number Instances"))
            {
                t.Start();

                // Empty Mark parameter to avoid duplicated values
                foreach (KeyValuePair<FamilyInstance, string> entry in instanceNumbers)
                {
                    Parameter instanceMark = entry.Key.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);
                    instanceMark.Set("");
                }

                // Populate Mark parameter
                foreach (KeyValuePair<FamilyInstance, string> entry in instanceNumbers)
                {
                    Parameter instanceMark = entry.Key.get_Parameter(BuiltInParameter.ALL_MODEL_MARK);
                    instanceMark.Set(entry.Value);
                }

                t.Commit();
            }
        }

        public static void InstanceFromToRoom(Dictionary<string, int> instanceInRoomCount,
                                              string roomNumber,
                                              Dictionary<FamilyInstance, string> instanceNumbers,
                                              FamilyInstance familyInstance)
        {
            // Letters to map from integers
            const string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

            // Classify family instances
            if (instanceInRoomCount.ContainsKey(roomNumber))
            {
                // Check instance count is one, a prefix will be added to the number
                if (instanceInRoomCount[roomNumber] == 1)
                {
                    // Retrieve instance with single count from the dictionary that stores instance-room relationship
                    FamilyInstance keyInstance = instanceNumbers.FirstOrDefault(x => x.Value == roomNumber).Key;
                    // Change single count instance prefix
                    instanceNumbers[keyInstance] = roomNumber + "-" + letters[instanceInRoomCount[roomNumber] - 1];
                    // Store current instance and room in dict
                    instanceNumbers[familyInstance] = roomNumber + "-" + letters[instanceInRoomCount[roomNumber]];
                }
                else
                {
                    // Store current instance and room in dict
                    instanceNumbers[familyInstance] = roomNumber + "-" + letters[instanceInRoomCount[roomNumber]];
                }
                // Increase the instance count in room
                instanceInRoomCount[roomNumber] = instanceInRoomCount[roomNumber] + 1;
            }
            else
            {
                // No instance is in room , add instance-room to dict
                instanceNumbers.Add(familyInstance, roomNumber);
                instanceInRoomCount.Add(roomNumber, 1);
            }
        }

Calling the refactored helper code

The next step is to create a push-button for the Number Windows command and a class to call from this button. These few lines to call our previous Helper methods will suffice:

namespace BIMiconToolbar.NumberWindows
{
    [TransactionAttribute(TransactionMode.Manual)]
    class NumberWindows : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            Document doc = commandData.Application.ActiveUIDocument.Document;
            BuiltInCategory builtInCategory = BuiltInCategory.OST_Windows;

            Helpers.Helpers.numberFamilyInstance(doc, builtInCategory);

            TaskDialog.Show("Number Windows", "Windows numbered");

            return Result.Succeeded;
        }
    }
}

Voila! Now we can number windows after room numbers quite easily.

Here is the compiled dll ready to use, just copy it and extract in the Revit Addins folder:

As usual, here is the repository for the source code:

BIMiconToolbar Github repository

Leave a Reply

Your email address will not be published. Required fields are marked *