Wednesday, June 2, 2010

[Revit API] Dynamic Model Update in the 2011 API

After completing the larger than normal task of converting my suite of Revit add-ins over to the new 2011 API, I’ve recently had the chance to sit down and start having a good play with the new features.

One feature which I have been particularly excited about is the new Element Level Events, also known as Dynamic Model Update. Basically, this lets you attach an event to a particular type of element, and when that element is changed, your code gets called, and can do various things. This is a feature which the team has clearly put a lot of thought into, and it opens Revit up to a whole new level of automation. Page 283 of the developers guide has a good introduction to this, and the example they use is changing all new walls to a certain type.

My first idea for using this tool is to solve a problem that some of the drafters at the company I work for have been complaining about, which is detail lines in Revit. When they want to insert a detail line they hit the keyboard shortcut, and select from a long list of different line styles, then draw the line. If they are doing lots of lines, they will scroll through that list many times – which is annoying. My idea was that they could tell my add-in what line style they want to insert, and then just draw detail lines without scrolling through the list each time.

The process would be this:

  • User runs the tool which registers an updater, and sets the line style.
  • When a new line is entered, that updater will be triggered
  • The updater will then convert the newly inserted line to the line style selected.

It was actually quite easy to get this working, which is a testament to how well this API is written.

First you need to create a class that implements IUpdater. Mine was called DetailLineUpdater. You then need to implement each of the members of that interface. These include:

  • Execute(UpdaterData data) – this is the actual method thats called when the updater is triggered, here is where I would change my line style.
  • GetAdditionalInformation() – just returns a description
  • GetChangePriority() – tells Revit the nature of what you will be doing in the Execute method. Revit uses this to determine in which order it should run each of the updaters.
  • GetUpdaterId() – returns an ID for the updater, which includes the AddInId and a GUID
  • GetUpdaterName() – a name for your updater. Make this something meaningful as it may get displayed to the user.

As you can see, these have some similarities to the kinds of things you need to tell Revit about your external commands.

Once you have your updater class, you need to do two things:

  1. Register it, using the UpdaterRegistry.RegisterUpdater(updater) method.
  2. Tell Revit when to trigger it.

For the second point, you need to use the UpdaterRegistry.AddTrigger method, which accepts three parameters.

  1. The id of the Updater
  2. A filter, which tells Revit which elements to attach to.
  3. A change type. This defines what actions on the filtered elements will trigger your updater. This could be insertion, deletion, parameter changes or geometry changes.

For my tool, I got the change type by using Element.GetChangeTypeElementAddition().

So now I have some code that will be run, whenever a certain type of element (in my case, elements with the category OST_Lines) is added. Here is a snapshot of the registration code:

DetailLineUpdater updater = new DetailLineUpdater(commandData.Application.ActiveAddInId);
                UpdaterRegistry.RegisterUpdater(updater);
                ElementCategoryFilter filter = new ElementCategoryFilter(BuiltInCategory.OST_Lines);
                UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), filter,Element.GetChangeTypeElementAddition());

Most of the IUpdater methods are trivial, but here is my Execute method:

        public void Execute(UpdaterData data)
        {
            foreach (ElementId addedElement in data.GetAddedElementIds())
            {
                DetailLine line = data.GetDocument().get_Element(addedElement) as DetailLine;

                foreach (Element style in line.LineStyles)
                {
                    if (style.Name.Equals("A_Certain_line_style"))
                        line.LineStyle = style;

                }
            }
            
        }

As you can see, its all fairly simple, and works well! However, there is a catch.

Once you use an updater on a model, it permanently stains it.

What do I mean by stain? It leaves a mark. Its not a huge deal, and doesn’t affect the model, but from a software developers point of view, its not ideal. If you run a tool, such as my line styling tool, and then someone else opens your model, or even links it into their project, they get a warning saying “the file … was modified by the third-party updater …… which is not currently installed”.

If you continue to edit the file, data maintained by …. will not be updated properly. This may create problems when … is later opened when …. is present.

wallwarning

The user can easily just click the second option ‘Do not warn about this updater again and continue working with the file’. But I believe every user will have to do this (have not confirmed this though, would love to hear if this is not the case), and I’d imagine, if you send your model to other firms to link in, they will all get it. This is not a good user experience, and would scare or confuse some users.

The intentions behind the warning are good, the Revit team obviously doesn’t want problems seen in AutoCAD where third party add-ins are so intertwined with projects that they cause a troubleshooting nightmare. But for simple time saving add-ins like my line styling tool, this is inconvenient. I’d love for there to be an option where developers can say that honestly if the updater is removed it will not cause any problems, and then that error will not show, hopefully next release!

All in all, this addition is great, and makes a range of great new add-ins to be made, but when your tool is not installed, users get this dialog, which could be confusing. If this doesn’t affect you, then I encourage you to get in there and have a play around, its easier than you’d think it would be, and I’m sure you’ll come up with some great ideas of how you could utilize it.