Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Efficient way to check if an element exists in a view

3 REPLIES 3
SOLVED
Reply
Message 1 of 4
IanMage1
2639 Views, 3 Replies

Efficient way to check if an element exists in a view

I'm creating a list of views that contain .dwg ImportInstance(s). For each view in the document, I'm using a FilteredElementCollector to get a list of elements meeting the criteria; if the list of elements meeting the criteria is not empty, then the view is added to the list:

foreach (Element viewElement in viewElements)
            {
                View view = (View)viewElement;
var stopwatch = new Stopwatch(); stopwatch.Start(); List<Element> elementsInView = new FilteredElementCollector(doc, view.Id).OfClass(typeof(ImportInstance)).Where(e => e.Category.Name.EndsWith(".dwg")).OfType<Element>().ToList(); stopwatch.Stop(); Debug.WriteLine(view.Name + ": " + stopwatch.ElapsedMilliseconds + "ms"); if(elementsInView.Count > 0) //if the current view contains at least 1 DWG ImportInstance, then the view is added to the list { viewsWithCAD.Add(view); continue; } }

The FilteredElementCollector can understandably take an upwards of 4000ms to collect elements from a view containing many elements. My goal is only to see if a single element exists in a view--not to collect all of the elements meeting the criteria; if I could make the FilteredElementCollector stop immediately after finding an element meeting the criteria, that would be helpful. 

 

I would appreciate any advice on how to achieve this more efficiently.

 

Thank you.

3 REPLIES 3
Message 2 of 4
FAIR59
in reply to: IanMage1

Stopping the collector at the first element:

Element e = new FilteredElementCollector(doc, view.Id).OfClass(typeof(ImportInstance)).FirstElement();

Possible Speed improvement:

  • Select all ImportInstances
  • check if its a DWG:
  • if no: exclude from collector in view-loop. (=> no need for category.Name check in loop)
  • if yes:   if dwg is ViewSpecific, i.e. is "2D annotation"in view, (=> ownerview contains dwg, and can be added to viewsWithCAD, and ownerview can be excluded from view-loop)

 

IEnumerable<ImportInstance> instances = new FilteredElementCollector(doc)
            	.OfClass(typeof(ImportInstance))
            	.Cast<ImportInstance>();
List<ElementId> toExclude = new List<ElementId>();
foreach(ImportInstance instance in instances)
{
    if ( !instance.Category.Name.EndsWith(".dwg"))
    {
    	toExclude.Add(instance.Id);
    	continue;
    }
    if( instance.ViewSpecific) // dwg only exists in ownerview
    {
    	View ownerview = doc.GetElement(instance.OwnerViewId) as View;
    	viewsWithCAD.Add(ownerview);
    	if( viewElements.Contains(ownerview)) viewElements.Remove(ownerview);
    }
}
            
foreach (Element viewElement in viewElements)
{
    View view = (View)viewElement;
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    Element e = null;
    if (toExclude.Count>0)
    {
	e = new FilteredElementCollector(doc, view.Id)
			.Excluding(toExclude)
			.OfClass(typeof(ImportInstance))
			.FirstElement();
    } else{
	e = new FilteredElementCollector(doc, view.Id)
			.OfClass(typeof(ImportInstance))
			.FirstElement();
    }
    stopwatch.Stop();
    Debug.WriteLine(view.Name + ": " + stopwatch.ElapsedMilliseconds + "ms");

    if(e!=null) //if the current view contains at least 1 DWG ImportInstance, then the view is added to the list
    {
        viewsWithCAD.Add(view);
    }
}
Message 3 of 4
jeremytammik
in reply to: IanMage1

Many thanks for the interesting question, and many thanks to Fair59 for yet another extremely knowledgeable and helpful solution!

 

I do keep pointing out that converting a filtered element collector to a List is an inefficient thing to do, if you can avoid it.

 

It forces the collector to retrieve all the data, convert it to the .NET memory space, duplicate it, costing time and space.

 

For the same reasons, it is much more efficient to test and apply as many filters as possible within the Revit memory space before passing any data across to .NET.

 

In this case, you can test the parameter values using a parameter filter instead of the LINQ post-processing that you are applying in you sample code snippet.

 

As Fair59 points out and we have discussed in the past, you can cancel a collector as soon as your target has been reached:

 

 

So, you can save time and space in several ways:

 

Use a parameter filter instead of LINQ post-processing

Do not convert to a List

 

Both of these force the filtered element collector to retrieve and return all results.

 

Here is an explanation of the various types of filters versus post-processing in .NET:

 

 

Here are some discussions and a benchmark of the results of using a parameter filter versus LINQ post-processing:

 

 

We also discussed the issue of finding all views displaying an element a couple of times in the past:

 

 

Cheers,

 

Jeremy

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

Message 4 of 4
jeremytammik
in reply to: IanMage1

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community