Is there a method of creating a slow filter with FilteredElementCollector on element name?

Is there a method of creating a slow filter with FilteredElementCollector on element name?

vhh7Z3D2
Contributor Contributor
1,674 Views
20 Replies
Message 1 of 21

Is there a method of creating a slow filter with FilteredElementCollector on element name?

vhh7Z3D2
Contributor
Contributor

I have a lot of code where filtering is done on the element name like:

AssemblyInstance assemblyInstance = new FilteredElementCollector(doc)
    .OfClass(typeof(AssemblyInstance))
    .Cast<AssemblyInstance>()
    .Where(assembly => assembly.Name == "SE51")
    .FirstOrDefault();

In previous versions of the API it seems like it was possible to make a slow filter on element name as explained here by using either of the these builtinparameters as filters:

 

  • BuiltInParameter.ALL_MODEL_NAME
  • BuiltInParameter.DATUM_TEXT
  • BulitInParameter.ELEM_NAME_PARAM

Example:

FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.DATUM_TEXT), "SE51");
ElementParameterFilter filter = new ElementParameterFilter(rule);

AssemblyInstance assemblyInstance = new FilteredElementCollector(doc)
    .OfClass(typeof(AssemblyInstance))
    .WherePasses(filter)
    .Cast<AssemblyInstance>()
    .FirstOrDefault();

However this does not work in 2022 and up.

Is there a way in the current API to make a filter on the name of an element?

0 Likes
Accepted solutions (1)
1,675 Views
20 Replies
Replies (20)
Message 2 of 21

MarryTookMyCoffe
Collaborator
Collaborator

I use FilterStringRule for it and have no problem with it, like

                 ParameterValueProvider provider = new ParameterValueProvider(idSP_ProjectName);
                 FilterStringRuleEvaluator evaluator = new FilterStringEquals();
                 FilterRule rule = new FilterStringRule(provider, evaluator, ProjectName);
                 ElementParameterFilter filterProjectName = new ElementParameterFilter(rule);
                 elements.WherePasses(filterProjectName);


Not work mean exception or just no result at all?

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 3 of 21

TripleM-Dev.net
Advisor
Advisor

Hi @vhh7Z3D2 ,

 

If you need the value of assembly that's returned by it's name property, "BuiltInParameter.DATUM_TEXT" doesn't exit for it. (use Revit Lookup what does return the name)

"ALL_MODEL_TYPE_NAME" doesn't return a value is empty as the instance isn't a type

"ELEM_TYPE_PARAM" does, but it's the type and you need te name, so I guess "CreateEqualsRule" doesn't compare by string/name, but by it's id. Which won't work.

 

That explanes why @MarryTookMyCoffe method of using FilterStringEquals forces it to use the name (AsStringValue) of the element to compare to a sting

 

 

0 Likes
Message 4 of 21

vhh7Z3D2
Contributor
Contributor

The code compiles and executes but, I am getting no results.

How are you getting the ElementId for a "Name" of an element in the ParameterValueProvider constructor?

0 Likes
Message 5 of 21

jeremy_tammik
Alumni
Alumni

Very probably, the name that you are searching for is squirrelled away in some parameter or other, or possibly a combination of them. So, all you need to do to reactivate your pre-2023 filtering code is to find out where the desired data is now stored and and access that instead of using the built-in parameters that apparently no longer contain it. One easy way to search for such things is to use RevitLookup and other database exploration tools. By the way, aren't parameter filters quick rather than slow? Anyway, the main point (that you are very correctly striving for) is to use either one of those instead of having to resort to the ultra-slow post-processing using the .NET Name property after the filtered element collector has finished.

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 6 of 21

vhh7Z3D2
Contributor
Contributor

I would like a filter on the name property of any element (if possible). In the example code I used assembly as an example. It could also be a column, wall or any other class decending from element.

I am not sure how @MarryTookMyCoffe s example work though. ParameterValueProviders constructor takes an ElementId, same as the original example posted. Which Id do I use here? Having taken a closer look at the builtinparameters, there dosn't seem to one for the name as it is a actually a property.

 

0 Likes
Message 7 of 21

jeremy_tammik
Alumni
Alumni

a closer look at the builtinparameters, there doesn't seem to one for the name as it is a actually a property

  

Yes, exactly. The Name property is populated in different ways from different parameters for different elements.

  

Therefore, to implement a filter for that property, you either need to determine which parameters to use for which element, which may be difficult, or else take the easy way out and determine which parameter works well for your use case for which element type, and then implement separate parameter filters for each of the different element types of interest.

  

Or, take some other creative and efficient approach. All roads lead to Rome. Some may be easier, and some may be faster. Pick, choose, be creative, and good luck solving this. Please let us know what you end up with, and please include benchmarking information comparing the different approaches. Thank you!

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 8 of 21

vhh7Z3D2
Contributor
Contributor

The name of elements seems to be stored in the name property according to RevitLookUp and should therefore be availible in any descendents of this class. I was wondering if perhaps maybe it would also be accessible through one of the parameters, making it possible to create a parameterfilter, rather than having to use linq. I might have misunderstood which data was accessed in the original post as mentioned by @TripleM-Dev.net though.

 

According to your own classification here WhereElementIsType, OfClass, OfCategory and similar are fast, parameterfilters are slow, and linq is last resort 🙂. What I am asking might not be possible though.

 

I am unsure as to what data is passed to the ParameterValueProvider constructor in @MarryTookMyCoffe s example, but I'll await and see.

0 Likes
Message 9 of 21

jeremy_tammik
Alumni
Alumni

parameterfilters are slow

  

Right you are!  Thank you for the reminder.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 10 of 21

TripleM-Dev.net
Advisor
Advisor

Hi @vhh7Z3D2,

 

If the name property returns the correct value, use that.

 

But first reduce the FilteredElementCollector items as much as possible using the Fast filters, like WhereElementIsNotElementType/ByView/Category/Pre-fetched id's etc..

 

Then compare against Name property with the wanted value.

 

I have no idea about it's performance (are you talking about 100.000 elements?), I have only used it on a couple of addins, for finding a certain family to use. And this was on a fairly reduced FilteredElementCollector list so finding the family was almost instant.

This was always by Category, and types only, so often limited to 100 items.

0 Likes
Message 11 of 21

vhh7Z3D2
Contributor
Contributor
Accepted solution
I'm working with a large codebase where multiple separate queries are beeing refactored into a common class library for optimization purposes.
The amount of elements is not really the problem. There are multiple methods that takes anywhere from 30 seconds to several minutes due to sheer volume of operations so any speed increase would be welcome.
As mentioned previously, filtering on name is featured in a lot of these queries, thuss why I was interested to know if creating a filter was possible.

I suppose a general filter is not feasible, but SOME classes do have their name set in a parameter like Level, Sheet and View so filters for those could be created.
0 Likes
Message 12 of 21

jeremy_tammik
Alumni
Alumni

I suppose a general filter is not feasible, but SOME classes do have their name set in a parameter like Level, Sheet and View so filters for those could be created.

     
I believe that ALL classes provide the required data in some parameter or other, so ALL classes can be equipped with a (slow) parameter filter that does the job. They may require adaptation for different built-in parameter enums depending on 'class of class'. I implemented a benchmark once comparing parameter filter versus slow filtering, and it is at least twice as fast.
   
Here is one such benchmark:
   
   
All my filtered element collec tor benchmarking and testing code is collected in the module CmdCollectorPerformance.cs:
  
   
Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 13 of 21

MarryTookMyCoffe
Collaborator
Collaborator

Excuse me for late replay, I can only sit on forum like once in two weeks.
You point out important thing. filtering don't use AsValueString as check. That is why you need to get symbol first and filter on it id. I believe that this should work for it(didn't test it but it is worth to try)

       /// <summary>
       /// 
       /// </summary>
       /// <param name="doc"></param>
       /// <param name="symbol">get base on SYMBOL_FAMILY_NAME_PARAM parameter in symbols</param>
       /// <returns></returns>
       public IList<Element> ElementFilter(Document doc, ElementId symbol)
       {
           
           var elements = new FilteredElementCollector(doc).WhereElementIsNotElementType().OfClass(typeof(FamilyInstance));
           ElementId parId = new ElementId(BuiltInParameter.ELEM_TYPE_PARAM);
           {
               ParameterValueProvider provider = new ParameterValueProvider(parId);
               FilterNumericEquals evaluator = new FilterNumericEquals();
               FilterElementIdRule rule = new FilterElementIdRule(provider, evaluator, symbol);
               ElementParameterFilter filterElementSymbol = new ElementParameterFilter(rule);
               elements.WherePasses(filterElementSymbol);
           }
           return elements.ToElements();
       }

I use Filtering for my DeleteManager mostly.

the name of Symbol where you can see it AsString is in parameter SYMBOL_FAMILY_NAME_PARAM
so you may first search for Symbols that you need(have in mind that symbols can have the same name for different families so take in account in what family symbol should be )
So this should be filter to

        public IList<Element> ElementTypeFilter(Document doc,string familyName, string name)
        {
            //SYMBOL_FAMILY_NAME_PARAM
            //SYMBOL_NAME_PARAM
            var elements = new FilteredElementCollector(doc).WhereElementIsElementType().OfClass(typeof(FamilySymbol));
            {
                ElementId parId = new ElementId(BuiltInParameter.SYMBOL_FAMILY_NAME_PARAM);
                ParameterValueProvider provider = new ParameterValueProvider(parId);
                FilterStringRuleEvaluator evaluator = new FilterStringEquals();
                FilterRule rule = new FilterStringRule(provider, evaluator, familyName);
                ElementParameterFilter filterSymbolName = new ElementParameterFilter(rule);
                elements.WherePasses(filterSymbolName);
            }
            {
                ElementId parId = new ElementId(BuiltInParameter.SYMBOL_NAME_PARAM);
                ParameterValueProvider provider = new ParameterValueProvider(parId);
                FilterStringRuleEvaluator evaluator = new FilterStringEquals();
                FilterRule rule = new FilterStringRule(provider, evaluator, name);
                ElementParameterFilter filterSymbolName = new ElementParameterFilter(rule);
                elements.WherePasses(filterSymbolName);
            }
            return elements.ToElements();
        }

 You should  only get one element from this.

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 14 of 21

vhh7Z3D2
Contributor
Contributor

Just to give an update on this: What I have so far is two options.

With the first one, class needs to be specified so is sort of similar to doing separate filters for each class, only collected into one method:

 

 

 public static class CollectorExtensions
 {
     public static FilteredElementCollector WhereElementNameIs<T>(this FilteredElementCollector collector, string name)
      where T : Element
     {
         var type = typeof(T);

         if (type == typeof(Level))
         {
             FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.LEVEL_NAME), name, true);
             ElementParameterFilter filter = new ElementParameterFilter(rule);
             return collector.WherePasses(filter);
         }
         else if (type == typeof(Material))
         {
             FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.MATERIAL_NAME), name, true);
             ElementParameterFilter filter = new ElementParameterFilter(rule);
             return collector.WherePasses(filter);
         }
         else if (type == typeof(ViewPlan))
         {
             FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.VIEW_NAME), name, true);
             ElementParameterFilter filter = new ElementParameterFilter(rule);
             return collector.WherePasses(filter);
         }
         //Add more here...
         else
         {
             throw new NotImplementedException();
         }
     }
}

 

 

 

Used like this:

 

 

var collector = new FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Levels)
    .OfClass(typeof(Level))
    .WhereElementNameIs<Level>("SE51");

 

 

 

Pros:

  • No need to remember what the name of the extension for specific class is. Like if you had an extension named "WhereLevelNameIs" and "WhereMaterialNameIs" you have to remember to use the correct one and maybe the name can be misleading as to which class it is intended for.
  • The class is known in the extension method, and the correct filter/logic can be applied.
  • If the class is not know, an error is reported.

Cons:

  • We need to specify the class in the calling method.

 

Option 2 is to not specify any class and apply all the filters we can think of:

 

 

public static FilteredElementCollector WhereElementNameIs(this FilteredElementCollector collector, string name)
{
    {   //Levels
        FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.LEVEL_NAME), name, true);
        ElementParameterFilter filter = new ElementParameterFilter(rule);
        collector = collector.WherePasses(filter);
    }
    {   //Materials
        FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.MATERIAL_NAME), name, true);
        ElementParameterFilter filter = new ElementParameterFilter(rule);
        collector = collector.WherePasses(filter);
    }
    {   //ViewPlan
        FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.VIEW_NAME), name, true);
        ElementParameterFilter filter = new ElementParameterFilter(rule);
        collector = collector.WherePasses(filter);
    }

    return collector;
}

 

 

 

Used like so:

 

 

var collector2 = new FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Levels)
    .OfClass(typeof(Level))
    .WhereElementNameIs("SE51");

 

 

 

Pros:

  • No need to specify class
  • Only one method name to remember when needing this filter

Cons:

  • No error reported if an element filter has not been implemented in the extension method.
  • Multiple WherePasses clauses may be slower than a single filter?
  • Multiple filters may return the wrong data if an element contains more than one of the BIPs we are looking in.
  •  
0 Likes
Message 15 of 21

jeremy_tammik
Alumni
Alumni

I prefer the first solution. The second looks very dodgy indeed to me. 

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 16 of 21

MarryTookMyCoffe
Collaborator
Collaborator

I agree first one will filter specific class with name(you can make even simpler with extra method).

 

Second one will only give something back if all this parameter have this name

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 17 of 21

TripleM-Dev.net
Advisor
Advisor

 

var collector = new FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_Levels)
    .OfClass(typeof(Level))
    .WhereElementNameIs<Level>("SE51");

 

Ok, if you want to use "WhereElementNameIs<Type>", why not include OfCategory AND OfClass inside "WhereElementNameIs".

Now you have to use "Level" 2 times, I always avoid something like that.

 

 

var collector = new FilteredElementCollector(doc)
    .WhereElementNameIs<Level>("SE51");

 

 

Also "WhereElementNameIs" itself, uses the same structure

 

FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.LEVEL_NAME), name, true);
             ElementParameterFilter filter = new ElementParameterFilter(rule);
             return collector.WherePasses(filter);

 

Only a different Builtinparameter.

I would build a static dictionary where the needed BuiltinParameter is tied to the Type

And to expand on that also include the BuiltinCategory?

 

With this the method can be easily expanded by adding a dictionary item.

 

And why not make a extension on the document type itself, retrieve a collection directly? (which can then be further filtered etc...)

 

var collector = doc.WhereElementNameIs<Level>("SE51");

 

 

Maybe I'm also way off.

- Michel

0 Likes
Message 18 of 21

vhh7Z3D2
Contributor
Contributor

The method is supposed to be universal so it can be used together with other collector methods. In our codebase there already are extensions like:

 

.WhereElementIsAssemblyInstance()

.WhereElementIsLevel()

.WhereElementIsFamilyInstance()

 

So your example in our codebase would look like this when its also nessesary to filter on level name:

FilteredElementCollector collector = new FilteredElementCollector(doc)
    .WhereElementIsLevel()
    .WhereElementNameIs("SE51");

 

Unfortunately we need to have the class type specified as well so the last line ends up beeing

.WhereElementNameIs<Level>("SE51");

Which isnt optimal, but close enough to be usefull.

 

As to your second question, having the extensions on the collector allows us to chain additional filters on should the need arise.  Sometimes we need to check for type (not class) as well, which is another extension method.

 

The internals of WhereElementNameIs could probably be optimised a bit, however there may be elements that require a different aproach.

To take an example, AssemblyInstance does not have a matching BIP for name as it seems to be inherited from its type (as far as I can tell), so to get it, I would have to look up the AssemblyType which does have a name in SYMBOL_NAME_PARAM. Then use the Id of that one to make a filter that matches ELEM_TYPE_PARAM which AssemblyInstance does have.

 

I tried it out and it does work, so the filtering logic for assembly instances ends up beeing:

 

else if (type == typeof(AssemblyInstance))
{
    FilterRule assemblyTypeRule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.SYMBOL_NAME_PARAM), name, true);
    ElementParameterFilter assemblyTypeFilter = new ElementParameterFilter(assemblyTypeRule);

    AssemblyType assemblyType = new FilteredElementCollector(document)
        .WherePasses(assemblyTypeFilter)
        .WhereElementIsAssemblyTypeAsEnumerable()
        .FirstOrDefault();

    if (assemblyType == null) return collector;

    FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.ELEM_TYPE_PARAM), assemblyType.Id);
    ElementParameterFilter filter = new ElementParameterFilter(rule);
    return collector.WherePasses(filter);
}

 

Where the "AsEnumerable" method is another homemade extension that simple does a cast:

 public static IEnumerable<AssemblyType> WhereElementIsAssemblyTypeAsEnumerable(this FilteredElementCollector collector)
 {
     return
         collector
         .WhereElementIsAssemblyType()
         .Cast<AssemblyType>();
 }

 

In order to this I do need the document though, so it will have to added as a parameter since there is no way to get it from the collector that is passed in, thus the final calling code becomes:

var collector = new FilteredElementCollector(document)
    .WhereElementNameIs<AssemblyInstance>(document, "SE51")
    .WhereElementIsAssemblyInstance();

 

0 Likes
Message 19 of 21

TripleM-Dev.net
Advisor
Advisor

What I meant was more in the form of, see code samples below:

 

    public static FilteredElementCollector WhereElementNameIs<T>(this Document doc, string name)
         where T : Element
        {
            var type = typeof(T);

            if (type == typeof(Level))
            {
                var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).OfClass(type);
                FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.LEVEL_NAME), name, true);
                ElementParameterFilter filter = new ElementParameterFilter(rule);
                return collector.WherePasses(filter);
            }
            else if (type == typeof(Material))
            {
                var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Materials).OfClass(type);
                FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.MATERIAL_NAME), name, true);
                ElementParameterFilter filter = new ElementParameterFilter(rule);
                return collector.WherePasses(filter);
            }
            else if (type == typeof(ViewPlan))
            {
                var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).OfClass(type);
                FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.VIEW_NAME), name, true);
                ElementParameterFilter filter = new ElementParameterFilter(rule);
                return collector.WherePasses(filter);
            }
            //Add more here...
            else
            {
                throw new NotImplementedException();
            }
        }

 And as the return is a FilteredElementCollector further calls can be made on it.

 

Called like:

var collector = doc.WhereElementNameIs<Level>("SE51"); // add any further filtering
//collector.WhereElementIsNotElementType(); // etc

 

But this would become a large method and not very flexibil.

I normally use smaller "Util" classes/methods that are then combined in another so expanding them is easier.

 

like this, so this for level can be called from document or FilteredElementCollector

public static FilteredElementCollector WhereNameIsOfLevel(this FilteredElementCollector collector, string name)
        { 
            FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.LEVEL_NAME), name, true);
            ElementParameterFilter filter = new ElementParameterFilter(rule);
            return collector.WherePasses(filter);
        }

        public static FilteredElementCollector WhereNameIsOfLevel(this Document doc, string name)
        {
            // option 1:
            //var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).OfClass(typeof(Level));
            //FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.LEVEL_NAME), name, true);
            //ElementParameterFilter filter = new ElementParameterFilter(rule);
            //return collector.WherePasses(filter);

            // Option 2: split function with different args, can then be used from document AND FilteredElementCollector for more flexibility.
            var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Levels).OfClass(typeof(Level));
            return collector.WhereNameIsOfLevel(name);
        }

 

The use of it in the larger context would be:

 public static FilteredElementCollector WhereElementNameIs<T>(this Document doc, string name)
        where T : Element
    {
        var type = typeof(T);

        if (type == typeof(Level)) // maybe use Select and not If-then-else
        {
            return doc.WhereNameIsOfLevel(name); // One line call
        }
        else if (type == typeof(Material))
        {
            var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Materials).OfClass(type);
            FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.MATERIAL_NAME), name, true);
            ElementParameterFilter filter = new ElementParameterFilter(rule);
            return collector.WherePasses(filter);
        }
        else if (type == typeof(ViewPlan))
        {
            var collector = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).OfClass(type);
            FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(BuiltInParameter.VIEW_NAME), name, true);
            ElementParameterFilter filter = new ElementParameterFilter(rule);
            return collector.WherePasses(filter);
        }
        //Add more here...
        else
        {
            throw new NotImplementedException();
        }
    }

 

 And with this you also can call, so you have options depending situation

var collector = doc.WhereNameIsOfLevel("SE51"); // Call the level method directly if suited for the app/routine.
collector.WhereElementIsNotElementType();

 

0 Likes
Message 20 of 21

vhh7Z3D2
Contributor
Contributor

It does not make sense to me to have a method on document called "WhereElementNameIs" returning a collector, but it is possible.

If going in that direction I would make a method returning all levels. Something like "document.Levels()" but that would have to a return type of IEnumerabel<Level> and thuss any further filtering would have to be done after the objects have been loaded into c#.

 

Thats why we choose to make all extensions on the collector itself, keeping in line with existing filters and to consolidate all filterings on specific classes into one common optimised method.

We may decide to split the "WhereNameIs" into smaller specific methods as you suggest, thuss avoiding having to specify the type.

 

Further integrating your previous suggestion about using a dictionary for simple 1:1 elements the method is much reduced in length:

public static class CollectorExtensions
{
    private static IDictionary<Type, BuiltInParameter> elementNameInBIP = new Dictionary<Type, BuiltInParameter>()
    {
        {typeof(Level), BuiltInParameter.DATUM_TEXT},
        {typeof(ReferencePlane), BuiltInParameter.DATUM_TEXT},
        {typeof(ViewSheet), BuiltInParameter.SHEET_NAME},
        {typeof(FamilySymbol), BuiltInParameter.ALL_MODEL_TYPE_NAME},
        {typeof(ElementType), BuiltInParameter.ALL_MODEL_TYPE_NAME},
        {typeof(AssemblyType), BuiltInParameter.ALL_MODEL_TYPE_NAME}
    };

    public static FilteredElementCollector WhereElementNameIs<T>(this FilteredElementCollector collector, Document document, string name)
     where T : Element
    {
        var type = typeof(T);

        if (elementNameInBIP.ContainsKey(type))
        {
            FilterRule rule = ParameterFilterRuleFactory.CreateEqualsRule(new ElementId(elementNameInBIP[type]), name, true);
            ElementParameterFilter filter = new ElementParameterFilter(rule);
            return collector.WherePasses(filter);
        }
        else
        {
            throw new NotImplementedException();
        }
    }
}

 

 

 

 

0 Likes