中文链接
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:
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 :-)

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