RevitAPI: How to use DMU (Dynamic Model Update) api?

中文链接

By Aaron Lu

We know Revit’s parametric modeling feature allows us to change an element and all related elements will be changed automatically to keep the model consistent, e.g. move a wall, the window or door on the wall will move accordingly.

But sometimes we want to add more association relations between elements, so that we have some customized propagation behavior, e.g. when extend a wall, another wall will become shorter, or when a component in link document is moved, another component in current document will also be moved. In this kind of situation, we can use DMU (Dynamic Model Update).

 

What is DMU? to be simple, it is a kind of event, or we can say it is an implementation of Sub-Pub design pattern, i.e. we register a callback function and when some event happens, the callback will be invoked. In DMU, the callback function is encapsulated in an interface named IUpdater, an updater is an instance of class which implements IUpdater.

Register

First let’s see the method(s) on how to register an updater, the signatures are:

public class UpdaterRegistry : IDisposable
{
public static void RegisterUpdater(IUpdater updater);
public static void RegisterUpdater(IUpdater updater, bool isOptional);
public static void RegisterUpdater(IUpdater updater, Document document);
public static void RegisterUpdater(IUpdater updater, Document document, bool isOptional);
}

Arguments and their meanings are:

Argument Meaning
IUpdater updater The instance with a callback function which will be invoked when something happens.
bool isOptional Whether it is optional or not, true means Revit won’t care if the updater is registered or not, false means the updater is very important, when it is missing, Revit will pop up warning dialog.
Document document Related docuument, if specify, then the updater will only apply to this document, otherwise, it will apply to the whole Revit application.

Implement IUpdater

Looking at the arguments of the above methods, we know that we should first create an instance of IUpdater.

How to implement IUpdater? Below is an code example:

public class ParameterUpdater : IUpdater
{
UpdaterId _uid;
public ParameterUpdater(Guid guid)
{
_uid = new UpdaterId(new AddInId(
new Guid("c1f5f009-8ba9-4f1d-b0fb-ba41a0f69942")), // addin id
guid); // updater id
}
public void Execute(UpdaterData data)
{
Func<ICollection<ElementId>, string> toString = ids => ids.Aggregate("", (ss, id) => ss + "," + id).TrimStart(',');
var sb = new StringBuilder();
sb.AppendLine("added:" + toString(data.GetAddedElementIds()));
sb.AppendLine("modified:" + toString(data.GetModifiedElementIds()));
sb.AppendLine("deleted:" + toString(data.GetDeletedElementIds()));
TaskDialog.Show("Changes", sb.ToString());
}
public string GetAdditionalInformation()
{
return "N/A";
}
public ChangePriority GetChangePriority()
{
return ChangePriority.FreeStandingComponents;
}
public UpdaterId GetUpdaterId()
{
return _uid;
}
public string GetUpdaterName()
{
return "ParameterUpdater";
}
}

Note that:

  • Creation of UpdaterId: the first argument is a Guid, which is guid of the addon/plugin registering the updater, which should be the same as the ClientId or AddinId defined in .addin file. e.g. below is the addin file of an ExternalCommand, so the first argument should be c1f5f009-8ba9-4f1d-b0fb-ba41a0f69942. The second argument is the guid of the updater itself, we can create one using the built-in guid generation tool of visual studio.
    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <RevitAddIns>
    <AddIn Type="Command">
    <Name>CommandB</Name>
    <ClientId>c1f5f009-8ba9-4f1d-b0fb-ba41a0f69942</ClientId>
    <Assembly>D:ADNTestbinDebugCommandB.dll</Assembly>
    <FullClassName>ApplicationB.CommandB</FullClassName>
    <VendorId>ADSK</VendorId>
    </AddIn>
    </RevitAddIns>
  • Execute function is the callback function, it will be invoked when something happens, we can get enough information from its argument UpdaterData, e.g. UpdaterData.GetDocument() returns the related Document object, GetAddedElementIds() returns the ids of added elements etc.. We can do a lot of things inside the callback function, for example, move or create an element, it is totally up to us.
  • We can’t use Transaction inside Execute method, because Execute is already in a transaction.
After implement IUpdater, we now can create and register an updater:
ParameterUpdater _updater = new ParameterUpdater(new Guid("{E305C880-2918-4FB0-8062-EE1FA70FABD6}"));
UpdaterRegistry.RegisterUpdater(_updater, true);

Here, the guid is created via builtin tool of VS

 

Trigger

Last thing is to tell Revit when what happens, the updater will be triggered, using AddTrigger method, signatures are:
public class UpdaterRegistry : IDisposable
{
public static void AddTrigger(UpdaterId id, ElementFilter filter, ChangeType change);
public static void AddTrigger(UpdaterId id, Document document, ElementFilter filter, ChangeType change);
public static void AddTrigger(UpdaterId id, Document document, ICollection<ElementId> elements, ChangeType change);
}

Arguments and meanings:

Argument Meaning
UpdaterId id id of updater
ElementFilter filter ElementFilter defines a set of elements, the updater will only be triggered when something happens to those elements
ICollection<ElementId> elements The updater will only be triggered when something happens to specific elements by designating the ids of them
Document document Only apply to a document
ChangeType change Specify the trigger condition, e.g. when parameter changed (Element.GetChangeTypeParameter) or when geometry changed (Element.GetChangeTypeGeometry) or any changes (Element.GetChangeTypeAny) etc.

 

If we want to trigger an updater when Area of an element is changed, we can write code like this:

var parameter = element.get_Parameter(BuiltInParameter.ROOM_AREA);
UpdaterRegistry.AddTrigger(_updater.GetUpdaterId(), doc,     new List<ElementId>() { new ElementId(197280)},     Element.GetChangeTypeParameter(parameter));

This is the whole DMU api usage workflow, when we change Area of element 197280, the Execute method will be invoked. Of course, we can do anything we want, but make sure there is no dead loop :-)

DMU_Area_Area_change

Note: we can use UpdaterRegistry.RemoveAllTriggers or UpdaterRegistry.RemoveDocumentTriggers to remove triggers and UpdaterRegistry.UnregisterUpdater to unregister updaters

 

 


Comments

3 responses to “RevitAPI: How to use DMU (Dynamic Model Update) api?”

  1. Hi Aaron, This looks helpful. Any chance you could provide a working solution? I cannot quite implement this. In any case, thanks. Dale

  2. Aaron Lu Avatar
    Aaron Lu

    Hi Dale, not sure what is your problem. I guess you did put the code in right place.
    all those code should be better put in IExternalApplication’s OnStartup method.

  3. I know this post is rather old, but how do you “cancel” the updater? For example, we have very stringent naming conventions for view and I have provided a prompt to users when the views are created to provide a Name and Title on Sheet, but I would like to make sure they do it. As such, if they “cancel” from the naming form, can I cancel the view creation and/or delete it? I did try to “delete” it using the AddedId but that threw an exception as I assume it can’t be deleted until the IUpdater transaction is committed. Thoughts?

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading