Introduction to UI Selection#
The Revit API offers a robust set of tools to let users select elements interactively instead of collecting them automatically. This is the key difference:
- FilteredElementCollector (previous article) = Finds elements automatically with code
- UI Selection (this article) = The user clicks on elements they want to process
When to use interactive selection:
- The user must choose visually what to modify
- The selection cannot be automated with filters
- You want visual confirmation before modifying elements
- You need flexibility — different users select different things
When to use collectors:
- The selection criteria are clear and automatable ("all walls on ground floor")
- You need speed — process hundreds of elements automatically
- The workflow must be repeatable without manual intervention
This is the third article in a three-part series on Revit API development:
- Getting Started with BIM Automation
- Element Collection with FilteredElementCollector
- UI Selection Methods (this article)
Required Libraries and Setup#
UI selection requires additional libraries compared to automatic collection:
1import clr2clr.AddReference('RevitAPIUI') # <-- Note: we add UI3import Autodesk4from Autodesk.Revit.UI import *5from Autodesk.Revit.UI.Selection import *6
7clr.AddReference('RevitServices')8import RevitServices9from RevitServices.Persistence import DocumentManager10
11# Get the current document and UI document12doc = DocumentManager.Instance.CurrentDBDocument13uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocumentKey difference:
doc= The Revit model (data)uidoc= The user interface (what the user sees)
All selection methods use uidoc, not doc.
Important Note for Dynamo#
WARNING: Most examples in this article work only in C# add-ins, not in Dynamo.
Dynamo doesn't directly support methods like PickObject() because:
- Dynamo is designed for visual programming, not code-based interaction
- Dynamo nodes handle input/output differently from add-ins
Alternatives in Dynamo:
- Use nodes like "Select Model Element" or "Select Model Elements"
- For advanced selections, create a separate C# add-in
- For prototyping, use PyRevit (standalone Python framework for Revit)
This article is useful if:
- You're learning Revit API to create C# add-ins in the future
- You want to understand how Dynamo selection nodes work internally
- You use PyRevit or similar frameworks that support
uidoc.Selection
1. Basic Element Selection#
1.1 Pick a Single Element#
The PickObject() method is the foundation of element selection in Revit API. It prompts the user to select a single element in the Revit interface.
1try:2 selected_reference = uidoc.Selection.PickObject(ObjectType.Element, "Select an element")3 element = doc.GetElement(selected_reference.ElementId)4 print(f"Selected element: {element.Name}")5 print(f"Category: {element.Category.Name}")6 print(f"ID: {element.Id}")7 8 # Get some basic parameters9 for param in element.Parameters:10 print(f"{param.Definition.Name}: {param.AsValueString()}")11except Exception as e:12 print(f"Selection cancelled or failed: {str(e)}")This example not only selects an element but also retrieves and prints various properties and parameters of the selected element.
Exercise: Modify the code to allow selection of a specific category (e.g., only walls). Hint: You'll need to use a custom ISelectionFilter.
1.2 Pick Multiple Elements#
When you need to select multiple elements, the PickObjects() method comes in handy:
1try:2 selected_references = uidoc.Selection.PickObjects(ObjectType.Element, "Select multiple elements")3 elements = [doc.GetElement(ref.ElementId) for ref in selected_references]4 5 category_count = {}6 for elem in elements:7 category = elem.Category.Name8 category_count[category] = category_count.get(category, 0) + 19 10 print(f"Selected {len(elements)} elements")11 for category, count in category_count.items():12 print(f"{category}: {count}")13except Exception as e:14 print(f"Selection cancelled or failed: {str(e)}")This example selects multiple elements and provides a summary of the selected elements by category.
Exercise: Extend the code to calculate and print the total volume of all selected elements that have a volume parameter.
2. Advanced Selection Techniques#
2.1 Selection by Rectangle#
The PickElementsByRectangle() method allows for a more visual selection process, similar to dragging a selection box in the Revit interface:
1try:2 selected_elements = uidoc.Selection.PickElementsByRectangle("Select elements by rectangle")3 4 levels = {}5 for elem_id in selected_elements:6 elem = doc.GetElement(elem_id)7 if hasattr(elem, 'Level'):8 level_name = elem.Level.Name if elem.Level else "No Level"9 levels[level_name] = levels.get(level_name, 0) + 110 11 print(f"Selected {len(selected_elements)} elements")12 for level, count in levels.items():13 print(f"Level {level}: {count} elements")14except Exception as e:15 print(f"Selection cancelled or failed: {str(e)}")This example selects elements within a rectangle and organizes them by level, providing a count of elements on each level.
2.2 Pick Box#
The PickBox() method creates a selection box based on two points, which can be useful for defining a 3D region:
1from Autodesk.Revit.DB import BoundingBoxIntersectsFilter, Outline2
3try:4 box = uidoc.Selection.PickBox(PickBoxStyle.Enclosing, "Select area")5 outline = Outline(box.Min, box.Max)6 bb_filter = BoundingBoxIntersectsFilter(outline)7 8 collector = FilteredElementCollector(doc).WherePasses(bb_filter)9 elements = list(collector)10 11 print(f"Found {len(elements)} elements within the picked box")12 category_count = {}13 for elem in elements:14 category = elem.Category.Name if elem.Category else "No Category"15 category_count[category] = category_count.get(category, 0) + 116 17 for category, count in category_count.items():18 print(f"{category}: {count}")19except Exception as e:20 print(f"Selection cancelled or failed: {str(e)}")This example uses the picked box to create a bounding box filter, then finds all elements that intersect with this box.
3. Custom Selection Filters#
Custom selection filters allow for fine-grained control over which elements can be selected:
1class CustomFilter(ISelectionFilter):2 def __init__(self, category_name, parameter_name, parameter_value):3 self.category_name = category_name4 self.parameter_name = parameter_name5 self.parameter_value = parameter_value6 7 def AllowElement(self, elem):8 if elem.Category and elem.Category.Name == self.category_name:9 parameter = elem.LookupParameter(self.parameter_name)10 if parameter and parameter.AsString() == self.parameter_value:11 return True12 return False13 14 def AllowReference(self, reference, point):15 return False16
17try:18 custom_filter = CustomFilter("Walls", "Comments", "Important")19 selected_elements = uidoc.Selection.PickObjects(20 ObjectType.Element, 21 custom_filter, 22 "Select important walls"23 )24 25 print(f"Selected {len(selected_elements)} important walls")26 for ref in selected_elements:27 elem = doc.GetElement(ref.ElementId)28 print(f"Wall ID: {elem.Id}, Name: {elem.Name}")29except Exception as e:30 print(f"Selection cancelled or failed: {str(e)}")This example creates a custom filter that only allows selection of walls with a specific comment value.
3.1 Category Filter#
A simpler filter that only allows selection of specific categories:
1class CategoryFilter(ISelectionFilter):2 def __init__(self, category_names):3 self.category_names = category_names if isinstance(category_names, list) else [category_names]4 5 def AllowElement(self, elem):6 if elem.Category:7 return elem.Category.Name in self.category_names8 return False9 10 def AllowReference(self, reference, point):11 return False12
13# Usage: Select only doors and windows14door_window_filter = CategoryFilter(["Doors", "Windows"])15selected = uidoc.Selection.PickObjects(16 ObjectType.Element, 17 door_window_filter, 18 "Select doors or windows"19)4. Working with Selected Views#
Retrieving and working with selected views in the project browser:
1def get_selected_views():2 selected_ids = uidoc.Selection.GetElementIds()3 selected_views = [4 doc.GetElement(id) for id in selected_ids 5 if doc.GetElement(id).GetType().Name.startswith("View")6 ]7 return selected_views8
9selected_views = get_selected_views()10print(f"Selected {len(selected_views)} views")11
12for view in selected_views:13 print(f"View Name: {view.Name}, Type: {view.ViewType}")14 15 # Count visible elements in each view16 collector = FilteredElementCollector(doc, view.Id).WhereElementIsNotElementType()17 element_count = collector.GetElementCount()18 print(f" Visible elements: {element_count}")19 20 # Check view-specific properties21 if hasattr(view, 'Scale'):22 print(f" Scale: 1:{view.Scale}")5. Point Selection#
Point selection is crucial for many operations, such as creating new elements or measuring distances:
1from Autodesk.Revit.DB import XYZ2
3def pick_points(prompt, count):4 points = []5 for i in range(count):6 try:7 point = uidoc.Selection.PickPoint(8 ObjectSnapTypes.Endpoints, 9 f"{prompt} (Point {i+1}/{count})"10 )11 points.append(point)12 except Exception as e:13 print(f"Point selection cancelled or failed: {str(e)}")14 return None15 return points16
17def distance_between_points(p1, p2):18 return p1.DistanceTo(p2)19
20points = pick_points("Select two points to measure distance", 2)21if points and len(points) == 2:22 distance = distance_between_points(points[0], points[1])23 print(f"Distance between points: {distance:.2f} feet")24 25 # Create a line element between the two points26 try:27 line = Line.CreateBound(points[0], points[1])28 doc.Create.NewDetailCurve(doc.ActiveView, line)29 print("Created a detail line between the points")30 except Exception as e:31 print(f"Failed to create detail line: {str(e)}")This example demonstrates how to pick multiple points, calculate the distance between them, and create a detail line.
6. User Interaction with TaskDialog#
Creating interactive dialogs enhances the user experience:
1def create_task_dialog(title, main_instruction, content, commands, verification_text=None):2 dialog = TaskDialog(title)3 dialog.MainInstruction = main_instruction4 dialog.MainContent = content5 6 for command in commands:7 dialog.AddCommandLink(command[0], command[1])8 9 if verification_text:10 dialog.VerificationText = verification_text11 12 return dialog13
14# Usage example15commands = [16 (TaskDialogCommandLinkId.CommandLink1, "Option 1: Select walls"),17 (TaskDialogCommandLinkId.CommandLink2, "Option 2: Select doors"),18 (TaskDialogCommandLinkId.CommandLink3, "Option 3: Cancel operation")19]20
21dialog = create_task_dialog(22 "Element Selection",23 "Choose what to select",24 "Your choice will determine which elements can be selected.",25 commands,26 "Remember my choice"27)28
29result = dialog.Show()30
31if result == TaskDialogResult.CommandLink1:32 print("User chose to select walls")33 # Proceed with wall selection34elif result == TaskDialogResult.CommandLink2:35 print("User chose to select doors")36 # Proceed with door selection37elif result == TaskDialogResult.CommandLink3:38 print("User cancelled the operation")39
40if dialog.WasVerificationChecked():41 print("User wants to remember this choice")7. Face and Edge Selection#
For more precise geometry selection:
1# Select a face2try:3 face_ref = uidoc.Selection.PickObject(ObjectType.Face, "Select a face")4 element = doc.GetElement(face_ref.ElementId)5 face = element.GetGeometryObjectFromReference(face_ref)6 7 print(f"Selected face from: {element.Name}")8 print(f"Face area: {face.Area:.2f} sq ft")9except Exception as e:10 print(f"Face selection failed: {str(e)}")11
12# Select an edge13try:14 edge_ref = uidoc.Selection.PickObject(ObjectType.Edge, "Select an edge")15 element = doc.GetElement(edge_ref.ElementId)16 edge = element.GetGeometryObjectFromReference(edge_ref)17 18 print(f"Selected edge from: {element.Name}")19 print(f"Edge length: {edge.ApproximateLength:.2f} ft")20except Exception as e:21 print(f"Edge selection failed: {str(e)}")8. Setting Pre-Selection#
You can also programmatically set the current selection:
1from Autodesk.Revit.DB import ElementId2from System.Collections.Generic import List3
4# Collect all walls5walls = FilteredElementCollector(doc).OfClass(Wall).ToElements()6
7# Create a list of ElementIds8wall_ids = List[ElementId]([wall.Id for wall in walls])9
10# Set the selection11uidoc.Selection.SetElementIds(wall_ids)12
13print(f"Pre-selected {len(wall_ids)} walls")9. Complete Example: Interactive Element Editor#
Here's a comprehensive example that combines multiple selection techniques:
1class ElementEditor:2 def __init__(self, doc, uidoc):3 self.doc = doc4 self.uidoc = uidoc5 6 def run(self):7 # Step 1: Show dialog to choose action8 dialog = TaskDialog("Element Editor")9 dialog.MainInstruction = "What would you like to do?"10 dialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Edit single element")11 dialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink2, "Edit multiple elements")12 dialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink3, "Edit elements in area")13 14 result = dialog.Show()15 16 if result == TaskDialogResult.CommandLink1:17 self.edit_single()18 elif result == TaskDialogResult.CommandLink2:19 self.edit_multiple()20 elif result == TaskDialogResult.CommandLink3:21 self.edit_in_area()22 23 def edit_single(self):24 try:25 ref = self.uidoc.Selection.PickObject(ObjectType.Element, "Select element to edit")26 element = self.doc.GetElement(ref.ElementId)27 self.show_element_info(element)28 except:29 print("Selection cancelled")30 31 def edit_multiple(self):32 try:33 refs = self.uidoc.Selection.PickObjects(ObjectType.Element, "Select elements to edit")34 elements = [self.doc.GetElement(ref.ElementId) for ref in refs]35 for elem in elements:36 self.show_element_info(elem)37 except:38 print("Selection cancelled")39 40 def edit_in_area(self):41 try:42 elements = self.uidoc.Selection.PickElementsByRectangle("Draw rectangle to select")43 for elem_id in elements:44 elem = self.doc.GetElement(elem_id)45 self.show_element_info(elem)46 except:47 print("Selection cancelled")48 49 def show_element_info(self, element):50 print(f"\n--- {element.Name} ---")51 print(f"ID: {element.Id}")52 print(f"Category: {element.Category.Name if element.Category else 'N/A'}")53
54# Run the editor55editor = ElementEditor(doc, uidoc)56editor.run()Conclusion#
This guide covers a wide range of Revit API selection methods, from basic element picking to advanced filtered selections and user interactions. The key to mastering Revit API selection is practice and experimentation.
Remember:
- Use custom filters to limit what users can select
- Always wrap selections in try-except blocks for graceful cancellation handling
- Combine selection methods with FilteredElementCollector for powerful workflows
- Provide clear prompts to guide users
What's Next?#
This concludes our three-part series on Revit API development:
- Getting Started with BIM Automation — Fundamentals and key concepts
- Element Collection with FilteredElementCollector — Finding elements programmatically
- UI Selection Methods — Interactive user selection (this article)
Questions or Feedback?
I'd love to hear your thoughts on this article. Reach out directly and let's start a conversation.
Follow me on LinkedIn for more BIM tips and updates




