Purge unused materials for another #RTCEUR API wish

Yesterday I looked at how to purge unused families and family types. Purging materials needs a different approach because they are not used directly in the project but are referenced by other objects.

Here is an approach that uses the DocumentChanged event, rolling back Transaction Groups, and other good things do find materials that can be deleted without elements being modified. It takes a bit of time to run because it is deleting all materials and undoing the deletions that modify other elements.

material-purge

public static int modifiedByDeleteMaterial = 0;
public static bool checkForPurgeMaterials = false;
public static Document _doc = null;
public static string materialName = "";
public void purgeMaterials()
{
    Document doc = this.ActiveUIDocument.Document;
    _doc = doc;
    Application app = doc.Application;
    app.DocumentChanged += documentChanged_PurgeMaterials;
    List<Element> materials = new FilteredElementCollector(doc).OfClass(typeof(Material)).ToList();
    string deletedMaterials = "";
    int unusedMaterialCount = 0;
    foreach (Element material in materials)
    {
        modifiedByDeleteMaterial = 0;
        materialName = material.Name + " (id " + material.Id + ")";
        using (TransactionGroup tg = new TransactionGroup(doc, "Delete Material: " + materialName))
        {
            tg.Start();
            using (Transaction t = new Transaction(doc, "delete material"))
            {
                t.Start();
                checkForPurgeMaterials = true;
                doc.Delete(material.Id);
                
                // commit the transaction to trigger the DocumentChanged event
                t.Commit();
            }
            checkForPurgeMaterials = false;
            
            if (modifiedByDeleteMaterial == 1)
            {
                unusedMaterialCount++;
                deletedMaterials += materialName + Environment.NewLine;
                tg.Assimilate();
            }
            else // rollback the transaction group to undo the deletion
                tg.RollBack();
        }
    }
    
    TaskDialog td = new TaskDialog("Info");
    td.MainInstruction = "Deleted " + unusedMaterialCount + " materials";
    td.MainContent = deletedMaterials;
    td.Show();

    app.DocumentChanged -= documentChanged_PurgeMaterials;
}

private static void documentChanged_PurgeMaterials(object sender, Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
    // do not check when rolling back the transaction group
    if (!checkForPurgeMaterials)
    {
        return;
    }
    
    List<ElementId> deleted = e.GetDeletedElementIds().ToList();
    List<ElementId> modified = e.GetModifiedElementIds().ToList();
    
    // for debugging
    string s = "";
    foreach (ElementId id in modified)
    {
        Element modifiedElement = _doc.GetElement(id);
        s += modifiedElement.Category.Name + " " + modifiedElement.Name + " (" +  id.IntegerValue + ")" + Environment.NewLine;
    }
    //TaskDialog.Show("d", materialName + Environment.NewLine + "Deleted = " + deleted.Count + ", Modified = " + modified.Count + Environment.NewLine + s);
    
    // how many elements were modified and deleted when this material was deleted?
    // if 1, then the material is unused and should be deleted
    modifiedByDeleteMaterial = deleted.Count + modified.Count;
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s