Announcements
Due to scheduled maintenance, the Autodesk Community will be inaccessible from 10:00PM PDT on Oct 16th for approximately 1 hour. We appreciate your patience during this time.
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: 

Using GetChangeTypeElementAddition to trigger on new FamilyInstance

3 REPLIES 3
Reply
Message 1 of 4
alisa_sidko_M3
316 Views, 3 Replies

Using GetChangeTypeElementAddition to trigger on new FamilyInstance

The end result I'm after is to propagate attached schema from Family (added to OwnerFamily at family editor stage) to the FamilyInstance when it first gets added to project. And I'm struggling to make it less trigger happy and more selective.

 

It's an adaptive family so I'm also trying to propagate it to ReferencePoints as well.

-using GetChangeTypeAny allows me to propagate to both;

-using GetChangeTypeGeometry allows me to propagate to FamilyInstance only;

-using GetChangeTypeElementAddition does nothing, it does get triggered when Family gets loaded into the project.

 

For filters I've tried:

LogicalOrFilter logicFilter = new LogicalOrFilter(
    new ElementClassFilter(typeof(FamilyInstance)),
    new ElementClassFilter(typeof(ReferencePoint))
);
LogicalOrFilter logicFilter = new LogicalOrFilter(
    new ElementCategoryFilter(BuiltInCategory.OST_GenericModel),
    new ElementCategoryFilter(BuiltInCategory.OST_AdaptivePoints)
);

Because of the remark on AddTrigger - "This method only works with CategoryFilter and ParameterFilter" which made no difference in this context.

 

I really want to avoid unnecessarily firing the IUpdater when I go to modify the FamilyInstance or its Adaptive Placement Points, and using GetChangeTypeElementAddition() is a logical choice, propagate the schema once and done, rather than have it get called upon any slight interaction or modification, but the way FamilyInstances get added to the project is bizarre to me, you'll only capture their ID's with GetModifiedElementIds() not GetAddedElementIds() same with ReferencePoints connected to the FamilyInstance.

 

Can the GetChangePriority be the culprit? I don't have a good grasp on it's purpose. But it didn't interfere when used with GetChangeTypeAny

public ChangePriority GetChangePriority() => ChangePriority.FreeStandingComponents;

Here's my Execute Implementation for the IUpdater:

public class MyElementAdditionUpdater : IUpdater
{
  private readonly UpdaterId _updaterId;

  public MyElementAdditionUpdater(AddInId addInId, Guid updaterGuid)
  {
    _updaterId = new UpdaterId(addInId, updaterGuid);
  }

  public void Execute(UpdaterData data)
  {
    Document doc = data.GetDocument();
    Schema schema = Schema.Lookup(schemaGuid);
    if (schema == null)
    {
      Debug.Print("Schema not found, exiting.");
      return; // Exit if schema doesn't exist
    }

    ElementClassFilter familyInstanceFilter = new ElementClassFilter(typeof(FamilyInstance));

    // Iterate over modified elements that are either FamilyInstance or ReferencePoint
    foreach (ElementId modifiedElementId in data.GetModifiedElementIds())
    {
      Element modifiedElement = doc.GetElement(modifiedElementId);
      if (!(modifiedElement is FamilyInstance || modifiedElement is ReferencePoint))
      {
        continue; // Skip if the element is neither FamilyInstance nor ReferencePoint
      }

      //GetDependentElements return FamilyInstance, from which Family schema could be copied
      var dependentElementIds = modifiedElement.GetDependentElements(familyInstanceFilter);

      foreach (ElementId dependentElementId in dependentElementIds)
      {
        FamilyInstance dependentFamilyInstance = doc.GetElement(dependentElementId) as FamilyInstance;
        if (dependentFamilyInstance == null)
        {
          continue; // Skip if the dependent element is not a FamilyInstance
        }

        Family family = dependentFamilyInstance.Symbol.Family;
        Entity familyEntity = family.GetEntity(schema);
        if (familyEntity == null)
        {
          Debug.Print($"Schema not found in family: {family.Name}, skipping.");
          continue; // Skip if the family does not have the schema
        }

        // Apply schema from family to both FamilyInstance and ReferencePoint
        Entity instanceEntity = new Entity(schema);
        bool schemaApplied = false;
        foreach (Field field in schema.ListFields())
        {
          if (familyEntity.Schema == null)
          {
            continue; // Skip if the schema field does not exist in the family entity
          }

          string value = familyEntity.Get<string>(field); // Assuming field values are string for simplicity
          instanceEntity.Set(field, value);
          schemaApplied = true;
        }

        if (schemaApplied)
        {
          Debug.Print($"Preparing to apply schema to element ID: {modifiedElementId}, Type: {modifiedElement.GetType().Name}.");

          try
          {
            modifiedElement.SetEntity(instanceEntity);
            Debug.Print($"Schema applied from Family {family.Name} to element ID: {modifiedElementId}.");
          }
          catch (Exception ex)
          {
            Debug.Print($"Exception applying schema: {ex.Message}");
          }
        }
      }
    }
  }

  public string GetAdditionalInformation() => "Handles addition of new elements with a specific schema.";
  public ChangePriority GetChangePriority() => ChangePriority.FreeStandingComponents;
  public UpdaterId GetUpdaterId() => _updaterId;
  public string GetUpdaterName() => "Custom Element Addition Updater";
}

The purpose of this attempt at propagation is so that I can have another IUpdater that will only get triggered in response to elements with schema that are getting manipulated by the user and to that end ExtensibleStorageFilter should do the job. But surely there's a better strategy to propagate Family Schema to it's FamilyInstance and other relevant objects.

Appreciate any help!

3 REPLIES 3
Message 2 of 4

Your question sounds rather complex and fine-tuned to me. Does the schema have to be applied immediately when the elements are added? Could you defuse the situation tremendously by implementing some other kind of check? Maybe you could scan the BIM and add the schema to elements lacking it on a regular basis when the model is closed, or every couple of minutes in a timer-triggered external event? Then it would not be quite as performance critical as it seems to be now, would it? 

  

People have complained in the past about DMUs using specific change types not working as expected, and being forced to use ChangeTypeAny to avoid those problems. 

  

The change priority defines the priority and thus the sort order of the modification:

  

  

The enumeration values are ordered in the order in which updaters associated to each priority will be run. For example, updaters associated to priority GridsLevelsReferencePlanes will run first, while updaters associated to priority Annotations will run last.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 3 of 4

Hi @jeremy_tammik, an honor!

 

Thank you for clarifying that the change priority isn’t impacting the targeting/filtering aspects of my implementation. It's somewhat comforting to know that the unpredictability with specific ChangeTypes and DMUs isn't just on my end, though it does add a layer of complexity to navigating around it.

 

As of right now my implementation is targeting single family element, and sure there's few workaround to be explored, but this code is being written with intend to expand to other families of the model in the future. The strategy was that if I could employs a non-interactable marker or proxy — that is essentially invisible to the end-user but detectable by the system then I could employ second IUpdater to automate some of the family modifications while keeping it light and efficient.

 

Right after a family instance is first placed, it's supposed to be automatically updated by my code, afterwards I foresee minor interaction with said FamilyInstance consisting of just slight adjustments compared to edits made to the rest of the project. That's why it's integral to make second IUpdater highly selective of when it fires off making use of ExtensibleStorageFilter highly desirable.

 

I had some thoughts about inverting the process of filtering, instead of looking at FamilyInstances and working my way to Family to check if it contains schema as I'm doing now, I could get single Family with said schema, and look at it's GetDependentElements. But that's likely irrelevant thought. As I would still need some efficient trigger for when FamilyInstance comes into being, which begs a question...

 

Why addition of FamilyInstance or it's ReferencePoints isn't being captured by GetAddedElementIds() at least from RevitLookup Event monitor? If there's some trick to getting GetAddedElementIds() to work on FamilyInstances as per the post here, it might be acceptable workaround to help exit updater if nothing of interest it getting added to the model.

 

Is there maybe some other way of selectively monitoring for when FamilyInstance get's added to the project rather than modified? Or even a totally different strategy to consider?

Main considerations:

  • The targeted FamilyInstance will rarely be interacted with as opposed to the rest of project;
  • The use case can be expanded to other families in the future;
  • The more foolproof I can make it the better;
  • The end-user might not follow the memo for correct order of operation if I opt for more manual approach (Get started by clicking on the tool in ribbon, instead of placing family on your own from library)
  • End user, might potentially rename or modify targeted parameters if I can't use schema as identifier.

Any further advice or ideas would be greatly appreciated. I'm willing to go deeper into rabbit hole of Revit API before I opt for an easy way out.

 

Thanks again!

Message 4 of 4

As I'm drilling down further into my code, I've reverted back to simple trigger.

 

UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), logicFilter,  Element.GetChangeTypeElementAddition());

 

Added few debug lines in the Execute()

 

 public void Execute(UpdaterData data)
 {
   Document doc = data.GetDocument();
   Schema schema = Schema.Lookup(schemaGuid);
   // Log all modified element IDs
   foreach (ElementId modifiedElementId in data.GetModifiedElementIds())
   {
     Element modifiedElement = doc.GetElement(modifiedElementId);
     Debug.Print($"Modified Element ID: {modifiedElementId}, Type: {modifiedElement.GetType().Name}");
   }

   // Log all added element IDs
   foreach (ElementId addedElementId in data.GetAddedElementIds())
   {
     Element addedElement = doc.GetElement(addedElementId);
     Debug.Print($"Added Element ID: {addedElementId}, Type: {addedElement.GetType().Name}");
   }

 

And surprisingly I'm getting the following in my Debug output

 

Updater has been registered.
Added Element ID: 694197, Type: FamilySymbol
.................
Added Element ID: 694750, Type: ReferencePoint
Added Element ID: 694834, Type: ReferencePoint

 

Updater gets registered when on DocumentOpened or FamilyLoadedIntoDocument we check for presence of schema and it exists.

 

The family in question is Generic Model Adaptive, with three Placement Points, but only 2 get recorded, so I tested the approach with simple Generic Model instead...

 

Added Element ID: 695845, Type: FamilySymbol
............
Added Element ID: 695861, Type: FamilyInstance

 

Going back to more encompassing GetChangeType

 

UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), logicFilter, ChangeType.ConcatenateChangeTypes(Element.GetChangeTypeGeometry(), Element.GetChangeTypeElementAddition()));

 

And my Debug Outputs:

 

Updater has been registered.
.............
Added Element ID: 694647, Type: ReferencePoint
Added Element ID: 694674, Type: ReferencePoint
Modified Element ID: 694221, Type: FamilyInstance
Preparing to apply schema to element ID: 694221, Type: FamilyInstance.
Schema applied from Family Stockpile Radial Adj to element ID: 694221.

 

But where's the 3rd point?

Switching to GetChangeTypeAny() and my debug now looks like this

Modified Element ID: 694213, Type: FamilyInstance
Modified Element ID: 694214, Type: ReferencePoint
Modified Element ID: 694215, Type: ReferencePoint
Modified Element ID: 694216, Type: ReferencePoint
Modified Element ID: 694217, Type: ReferencePoint
Preparing to apply schema to element ID: 694213, Type: FamilyInstance.
Schema applied from Family Stockpile Radial Adj to element ID: 694213.
Preparing to apply schema to element ID: 694214, Type: ReferencePoint.
Schema applied from Family Stockpile Radial Adj to element ID: 694214.
Preparing to apply schema to element ID: 694215, Type: ReferencePoint.
Schema applied from Family Stockpile Radial Adj to element ID: 694215.
Preparing to apply schema to element ID: 694216, Type: ReferencePoint.
Schema applied from Family Stockpile Radial Adj to element ID: 694216.
Preparing to apply schema to element ID: 694217, Type: ReferencePoint.
Schema applied from Family Stockpile Radial Adj to element ID: 694217.

Just how do I tackle Generic Family Adaptive 😫?

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

Post to forums  

Rail Community


Autodesk Design & Make Report