We tried to achieve the same goal using two ways before, one of which uses the ElementParameterFilter and its various string rules and evaluators, and the other tries to create some custom filter string rules and evaluators in both C# and VB.NET, but all failed due to kinds of limitations respectively.
In this post, let us revisit the topic and get it done in a simple, nice and right way.
To make a simple comparison, the code using the ElementParameterFilter approach has been appended again:
public static ICollection<ElementId> GetRoomsNumberInRange(RvtDocument doc, string low, string high)
{
ParameterValueProvider provider = new ParameterValueProvider(new ElementId((int)BuiltInParameter.ROOM_NUMBER)); //THE ELEM_ROOM_NUMBER IS NOT THE RITHT PARAMETER!!!
FilterStringRule rule1 = new FilterStringRule(provider, new FilterStringGreaterOrEqual(), low, false);
FilterStringRule rule2 = new FilterStringRule(provider, new FilterStringLessOrEqual(), high, true);
ElementParameterFilter filter1 = new ElementParameterFilter(rule1);
ElementParameterFilter filter2 = new ElementParameterFilter(rule2);
return
(new FilteredElementCollector(doc))
//.OfClass(typeof(Room)) //THE ROOM CLASS DOES NOT WORK!!
.OfCategory(BuiltInCategory.OST_Rooms)
.WherePasses(filter1)
.WherePasses(filter2)
.ToElementIds();
}
Now, here is our own way:
public static ICollection<ElementId> GetRoomsTrueNumberInRange(RvtDocument doc, int low, int high)
{
List<ElementId> finalRooms = new List<ElementId>();
foreach (Element room in new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms).ToElements())
{
Parameter param = (from Parameter p in room.Parameters where p.Id == new ElementId((int)BuiltInParameter.ROOM_NUMBER) select p).FirstOrDefault();
if (param != null)
{
int num;
if ( int.TryParse(param.AsString(), out num) && num >= low && num <= high)
finalRooms.Add(room.Id);
}
}
return finalRooms;
}
Once again, as similarly demonstrated previously, only a very basic category filter is used, and no other filter providers, rules, or evaluators at all. The code is concise. The logic is simple and clear. The very thing is that no existing filter rules or evaluators can really meet this simple but common need.
The following test code can be used to compare the output and the performance of the two approaches:
…
DateTime time = DateTime.Now;
ICollection<ElementId> roomids1 = GetRoomsNumberInRange(CachedDoc, "10", "20");
TimeSpan timespan = DateTime.Now - time;
string message = string.Format("{0} rooms are found in {1} milliseconds using FilterStringRule:\n\t", roomids1.Count, timespan.TotalMilliseconds);
foreach (ElementId id in roomids1)
message += (CachedDoc.get_Element(id) as Room).Number + ", ";
time = DateTime.Now;
ICollection<ElementId> roomids2 = GetRoomsTrueNumberInRange(CachedDoc, 10, 20);
timespan = DateTime.Now - time;
message += string.Format("\n\n{0} rooms are found in {1} milliseconds using our own way:\n\t", roomids2.Count, timespan.TotalMilliseconds);
foreach (ElementId id in roomids2)
message += (CachedDoc.get_Element(id) as Room).Number + ", ";
MessageBox.Show(message, "Find Rooms With Numbers In Range [10, 20]");
…
Here is the report for a sample test model:
As can be seen, the ElementParamererFilter, the FilterStringRule and its various evaluators wrongly report something not expected by us, the room number 2. Our own way reports exactly as expected, not more and not less.
And another part is that our own way takes less time than the ElementParameterFilter way. It is really amazing, isn’t it?
It might have something to do with element filter cache or something like that, but even if our own way takes more milliseconds like two times or three times more, it is not a big deal at all, is it? The most important point anyway is that we can get the right things done this time.
ElementParameterFilter Creator of RevitAddinCoder can help create code combining various existing filter rules and evaluators to filter element parameters.
Recent Comments