Select same elements in group instances

Select same elements in group instances

baleti3266
Advocate Advocate
105 Views
1 Reply
Message 1 of 2

Select same elements in group instances

baleti3266
Advocate
Advocate

Does anyone know any good way to add the same elements across group instances to current selection? I mean like this:

baleti3266_0-1754004178323.png

here are 3 groups and 3 selected elements are the same elements across these groups


I thought that I got it to work at first: revit-scripts/revit-scripts/SelectSameInstancesInGroups.cs at 8d876bea5c4f53ce21c727c59e075b21c61dde...

but then it failed to select some instances correctly when a group was modified (presumably some elements were "excluded"), so I started experimenting with getting centroids of these elements and comparing them, but didn't manage to get it to work (either false negatives or false positives) - if anyone knew how to get a transformation of a model group that would be really helpful as well.

 

Does anyone know a better approach? Something that takes such excluded elements into account?

code from link above copied here for reference:

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Collections.Generic;
using System.Linq;

[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class SelectSameInstancesInGroups : IExternalCommand
{
    public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
    {
        try
        {
            var uidoc = commandData.Application.ActiveUIDocument;
            var doc = uidoc.Document;
            
            // Get currently selected elements using the extension method from SelectionModeManager
            var selectedIds = uidoc.GetSelectionIds();
            
            if (!selectedIds.Any())
            {
                TaskDialog.Show("Selection", "Please select at least one element that belongs to a group.");
                return Result.Cancelled;
            }
            
            // Dictionary to map GroupType to list of indices of selected elements within that group type
            var groupTypeToIndices = new Dictionary<ElementId, HashSet<int>>();
            
            // Dictionary to cache group instances by type
            var groupTypeToInstances = new Dictionary<ElementId, List<Group>>();
            
            // Process selected elements
            foreach (var selectedId in selectedIds)
            {
                var element = doc.GetElement(selectedId);
                if (element == null) continue;
                
                // Skip if not in a group
                if (element.GroupId == ElementId.InvalidElementId) continue;
                
                var group = doc.GetElement(element.GroupId) as Group;
                if (group == null) continue;
                
                var groupTypeId = group.GetTypeId();
                
                // Get member index efficiently
                var memberIds = group.GetMemberIds();
                int memberIndex = -1;
                
                // Find index by comparing ElementIds directly (much faster than loading elements)
                for (int i = 0; i < memberIds.Count; i++)
                {
                    if (memberIds.ElementAt(i) == selectedId)
                    {
                        memberIndex = i;
                        break;
                    }
                }
                
                if (memberIndex == -1) continue;
                
                // Store the member index for this group type
                if (!groupTypeToIndices.ContainsKey(groupTypeId))
                {
                    groupTypeToIndices[groupTypeId] = new HashSet<int>();
                }
                groupTypeToIndices[groupTypeId].Add(memberIndex);
                
                // Cache group instances for this type
                if (!groupTypeToInstances.ContainsKey(groupTypeId))
                {
                    // Get all instances of this group type in one query
                    groupTypeToInstances[groupTypeId] = new FilteredElementCollector(doc)
                        .OfClass(typeof(Group))
                        .Cast<Group>()
                        .Where(g => g.GetTypeId() == groupTypeId)
                        .ToList();
                }
            }
            
            if (!groupTypeToIndices.Any())
            {
                TaskDialog.Show("Selection", "No selected elements belong to groups.");
                return Result.Cancelled;
            }
            
            // Collect all matching elements
            var allElementIds = new HashSet<ElementId>(selectedIds);
            
            // For each group type with selected elements
            foreach (var kvp in groupTypeToIndices)
            {
                var groupTypeId = kvp.Key;
                var memberIndices = kvp.Value;
                var groupInstances = groupTypeToInstances[groupTypeId];
                
                // For each group instance of this type
                foreach (var groupInstance in groupInstances)
                {
                    var memberIds = groupInstance.GetMemberIds();
                    
                    // Add elements at the stored indices
                    foreach (int index in memberIndices)
                    {
                        if (index >= 0 && index < memberIds.Count)
                        {
                            allElementIds.Add(memberIds.ElementAt(index));
                        }
                    }
                }
            }
            
            // Set the new selection using the extension method from SelectionModeManager
            uidoc.SetSelectionIds(allElementIds.ToList());
            
            // Report results
            int additionalElements = allElementIds.Count - selectedIds.Count;
            string resultMessage = $"Selected {allElementIds.Count} elements total.\n" +
                                 $"Found {additionalElements} additional matching elements in groups.";
            
            TaskDialog.Show("Selection Complete", resultMessage);
            
            return Result.Succeeded;
        }
        catch (Exception ex)
        {
            message = ex.Message;
            return Result.Failed;
        }
    }
}

 

0 Likes
106 Views
1 Reply
Reply (1)
Message 2 of 2

longt61
Advocate
Advocate

From the issue arises during your work and your sample code, I think you lack the logic to determine how 2 elements are similar. Your are assuming all groups are the same, including the members, the order of the members, the modification of the groups, etc... That is why you use the index of member Id to determine if 2 elements are similar. This method works only if you make multiple copy from an original group, but might not work if the groups are modified, or user adds or removes elements of some groups, or each group contains elements which are different from the other groups.
Therefore, I think the most important thing to do is to finalize the criteria for 2 similar elements, whether they share the same length, the same particular custom parameter value, the same family, etc.. With that logic figured out, the rest is quite simple. You can see the code below as an example:

    [Transaction(TransactionMode.Manual)]
    public class CmdHighlightAcrossGroup : IExternalCommand
    {
        private UIDocument _uiDoc;
        private Document _doc;
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            _uiDoc = commandData.Application.ActiveUIDocument;
            _doc = _uiDoc.Document;

            Dojob();

            return Result.Succeeded;
        }

        private void Dojob()
        {
            var seed = RevitCommon.PickElement(_uiDoc, null, "Pick an element inside a group");
            if (seed != null)
            {
                var groups = GetAllGroup();
                var ids = GetMatchId(seed, groups);
                RevitCommon.Highlight(_uiDoc, ids);
            }
            else
            {
                CommonDialogs.ShowWarning("No element is selected");
            }
        }

        private List<ElementId> GetMatchId(Element seed, List<Group> groups)
        {
            return groups.SelectMany(x => x.GetMemberIds())
                        .Select(x => _doc.GetElement(x))
                        .Where(x => IsSameElement(seed, x))
                        .Select(x => x.Id)
                        .ToList();
        }

        // you can modify this method to implement your own matching logic
        private bool IsSameElement(Element seed, Element other)
        {
            return seed != null 
                && other != null
                   && seed.GetType() == other.GetType()
                   && seed.Category?.Id == other.Category?.Id
                   && seed.Name == other.Name;
        }

        private List<Group> GetAllGroup()
        {
            return new FilteredElementCollector(_doc)
                    .WhereElementIsNotElementType()
                    .OfCategory(BuiltInCategory.OST_IOSModelGroups)
                    .OfClass(typeof(Group))
                    .Cast<Group>()
                    .ToList();
        }
    }
0 Likes