We repeatedly looked at ways to detect and purge unused elements. A list of some previous discussions of the topic was given last time we looked at purge and detecting an empty view.
Matt Taylor, associate and CAD developer at WSP, was the first to congratulate on The Building Coder's ten-year anniversary.
He now adds something really special to celebrate this:
GetPurgeableElements
I’m sharing with you a new discovery of mine.
Apparently, nobody has previously publicly discovered a simple and effective way of purging all unused elements.
I now found one:
I have successfully used
the Performance Adviser to
do a similar job to the native Purge Unused
command.
Please refer to my RevitPurgeUnused GitLab repository.
While the code will compile back to Revit 2012, it actually throws an InternalException
for versions 2012-2016 (in my experience).
It doesn’t do a perfect job (e.g., it doesn’t purge materials and material assets), but it is very, very good, and quite fast.
I also added a note of my solution to some of the existing threads on this topic in the Revit API discussion forum:
Very many thanks to Matt for sharing this solution to one of the top developer wish list items!
Here is Matt's VB.NET code
in PurgeTool.vb,
defining the long-sought-after GetPurgeableElements
method:
#Region "Imported Namespaces" Imports System Imports System.Collections.Generic Imports Autodesk.Revit.DB #End Region Public Class PurgeTool ''' <summary> ''' The guid of the 'Project contains unused families and types' PerformanceAdviserRuleId. ''' </summary> Const PurgeGuid As String = "e8c63650-70b7-435a-9010-ec97660c1bda" ''' <summary> ''' Get all purgeable elements. ''' Intended for Revit 2017+ as versions up to and including Revit 2016 throw an InternalException. ''' </summary> ''' <param name="doc"></param> ''' <param name="purgeableElementIds"></param> ''' <returns>True if successful.</returns> Shared Function GetPurgeableElements(doc As Document, ByRef purgeableElementIds As ICollection(Of ElementId)) As Boolean purgeableElementIds = New List(Of ElementId)() Try 'create a new list of rules. Dim ruleIds As IList(Of PerformanceAdviserRuleId) = New List(Of PerformanceAdviserRuleId) Dim ruleId As PerformanceAdviserRuleId = Nothing 'find the intended rule. If GetPerformanceAdvisorRuleId(PurgeGuid, ruleId) Then 'add the rule to the new list. ruleIds.Add(ruleId) Else 'cannot find rule. Return False End If 'execute our chosen rule only. Dim failureMessages As IList(Of FailureMessage) = PerformanceAdviser.GetPerformanceAdviser().ExecuteRules(doc, ruleIds) If failureMessages.Count > 0 Then 'If there are any purgeable elements, we should have a failure message. 'the failure message should have a collection of failing elements - set to our byref collection purgeableElementIds = failureMessages.Item(0).GetFailingElements End If 'no errors - return true. Return True Catch ex As Autodesk.Revit.Exceptions.InternalException 'this exception gets thrown a lot in earlier versions of Revit - up to and including Revit 2016. End Try 'likely thrown an internal exception Return False End Function ''' <summary> ''' Find a PerformanceAdviserRuleId with a guid that matches a supplied guid. ''' </summary> ''' <param name="guidStr"></param> ''' <param name="ruleId"></param> ''' <returns>true if successful, along with the rule as a byref.</returns> Private Shared Function GetPerformanceAdvisorRuleId(ByVal guidStr As String, ByRef ruleId As PerformanceAdviserRuleId) As Boolean ruleId = Nothing Dim guid As Guid = New Guid(guidStr) For Each rule As PerformanceAdviserRuleId In PerformanceAdviser.GetPerformanceAdviser().GetAllRuleIds 'check if the rule Id matches our rule guid If rule.Guid.Equals(guid) Then 'it does - set rule to our byref object ruleId = rule Return True End If Next 'failed to find the rule matching our guid. Return Nothing End Function End Class
The result is used like this in the external command defined by PurgeUnused.vb:
Dim purgeableElements As ICollection(Of ElementId) = Nothing If PurgeTool.GetPurgeableElements(doc, purgeableElements) AndAlso purgeableElements.Count > 0 Then Using transaction As New Transaction(doc, "Purge Unused") transaction.Start() doc.Delete(purgeableElements) transaction.Commit() Return Result.Succeeded End Using Else Return Result.Failed End If