Friday, March 26, 2010

Revit API 2011: Upgrading Existing Add-Ins

(Part of our Revit API 2011 Series)
If there is a dark side to all of the positive changes in the 2011 API – it is that the amount of re-work required for existing add-ins is FAR more than previous upgrades. I’m tired just writing this post…

While previous upgrades have been fairly painless, in 2011 you’re up against:

  • Massive namespace renaming/restructuring.
  • Deprecation and replacement of many of the fundamental element search and iteration APIs.
  • Fundamentally new transaction and regeneration mechanisms
  • Non-trivial changes to the fundamental XYZ, UV and ElementId class
  • Changes to the Element.ObjectType structure.
  • Replacement of the Analytical Model classes
  • New Event Model
  • New Selection Mechanisms
  • Ribbon API changes
  • Print API changes
  • Re-work of the gbXML class
  • Installer changes (if you want to take advantage of new capabilities)

Massive Namespace Restructuring

The factory has taken this opportunity to completely re-work the namespace hierarchy into something which more closely matches Revit’s internals – as well as to clean house, straighten up, and simplify some things.

While I would argues that this is probably a good thing in the long term, it will require a bit of work to recompile your code.

While I won’t cover the namespaces in detail here, to give you an idea of what you’re looking at:

  • Autodesk
    • Autodesk.Revit
          • Autodesk.Revit.UI (where all the User Interface and command/application integration classes landed)
          • Autodesk.Revit.DB   (this is the container for most core elements, geometry, parameters, and other concepts)
              • Autodesk.Revit.DB.Architecture ( contains architecture-specific elements like Rooms, RoomTags, Gutter, Fascia, etc)
              • Autodesk.Revit.DB.Electrical (contains electrical-related elements and concepts, from CableTray to DemandFactor)
              • Autodesk.Revit.DB.Events (contains the event arguments for all the different kinds of events)
              • Autodesk.Revit.DB.Mechanical (Spaces, Zones, SpaceTags, Ductwork, etc)
              • Autodesk.Revit.DB.Plumbing ( Piping, PipingSystems, etc)
              • Autodesk.Revit.DB.Structure ( Analytical Model, Loads, Rebar, Trusses, etc)

Other key re-naming issues:

  • the Symbol class has been re-named to “ElementType”
  • the old Geometry.Element class has been renamed to GeometryElement
  • the old Geometry.Instance class has been renamed to GeometryInstance
  • These are further described in the “What’s New” section of the API Help File

Autodesk is also providing in the SDK a spreadsheet of all of the class renames, and their corresponding new names. It is a long list :).

Where Did My ElementIterator Go?

Autodesk has taken somewhat of an unusual step in 2011 – that of ripping and replacing all of the three different element search and iteration schemes from before 2011 and replacing them with the new FilteredElementCollector. The Document.Elements collection, the Document.get_Elements( type ) and the filter mechanism have all been removed.

See our separate post on the new mechanism (which is quite superior) – but it may take a minute or two to re-work any of your code which iterates through elements in the document (which almost every command does at least once).

Also note – the View.Elements collection has also been re-worked to the new scheme.

The New Transaction Mechanisms

There are fundamentally new transaction mechanisms that you will likely need to deal with (unless your application was very straightforward). Commands are now attributed with one of two Transaction modes:

  • Automatic – like what has existed historically.
  • Manual – the developer must control their own transactions explicitly.

For upgrading an existing application, you’ll probably want to stay with Automatic because it most closely matches the prior release behavior. However, if you had been using transactions within your code in the past (i.e. Document.BeginTransaction) you must replace these with the new “SubTransaction” classes.

//New SubTransaction approach

SubTransaction subTr = new SubTransaction( myDoc );
try
{
    subTr.Start();
   // do something
    subTr.Commit()
}
catch (Exception ex)
{
    TaskDialog.Show(“Error”, “An unexpected error was encountered: “ + ex.GetType().Name + “: “ + ex.Message);
    if (subTr.HasStarted()) subTr.Rollback();
}

Developer TechTip:
If you go the manual transaction approach, don’t forget to name your transaction. This name shows up in the “Undo” box, but because there is a constructor which does not require a name, it’s easy to forget. And Revit throws a nasty exception at you if you forget to name it.

The New Regeneration Mechanism

All commands in Revit are now attributed with another attribute: RegenerationMode:

  • Automatic – same way Revit has worked traditionally – after every action in Revit, the model is regenerated (unless you’re using SuspendUpdate). Solid, but slow on occasion. Also – Autodesk has indicated that this mode is going away in the future.
  • Manual – you are in complete control of your own regeneration (powerful and fast but occasionally painful).

With manual regeneration, you issue a Document.Regenerate any time that you want the project to regenerate. Seems easy enough, right? Well… First you have to understand when you NEED to regenerate. There are a variety of cases, particularly when you are creating or modifying geometry, that the changes will not be propagated until you do the regenerate. For example, if you create a line, you are unable to query for the reference to that line until the document has been regenerated.

Why it can be challenging: If you’ve got library code that you re-use in multiple commands. Are you currently in Manual Regeneration mode or not? it depends on how you entered the current command. If you call Document.Regenerate and you’re already in Automatic mode, Revit will throw an exception (seems like a harmless problem – factory guys, chill out!). To further complicate things, if your code is being called as part of an event callback, I believe that it is ALWAYS going to be in Automatic Transaction mode (even if the whole thing is in the context of a manual transaction command). So there’s some potential trickiness to deal with.

XYZ, UV and ElementId - All Change to be Unchangeable!

OK – this is just cruel… I’d swear that this is the third change to the XYZ class in three releases! And it’s worse than previous issues. In 2011, you can no longer do the following code:

myPoint.X = 10;

What?? why not? Well, Autodesk has made the XYZ, UV, and ElementId classes immutable – they cannot be changed once they are created. There’s a reasonable reason behind this from a framework design perspective – because it implies that you could do something that you really couldn’t. For example, if you did:

myWall.Location.Point.X = myWall.Location.Point.X + 1;

What would this do? You would think that it might move the wall 1 foot in the X direction. In fact, it does not. The Point here was a COPY of the actual data – so changing the X value did nothing.

To do what you wanted to do above, you had to:

XYZ newPt = new XYZ( myWall.Location.Point.X + 1,
                               myWall.Location.Point.Y, 
                               myWall.Location.Point.Z);
myWall.Location.Point = newPt;

In the case of the new immutable XYZ class, the only way to modify an XYZ, or UV, or ElementId is to construct a new version. So to modify an XYZ point, you must:

myPoint = new XYZ( myPoint.X +1, myPoint.Y, myPoint.Z);

If you did much with XYZ coordinates in the past, this likely means a bunch of grumbling, painful code re-writing and re-testing.

Oh – and ElementId? they changed the name of the actual Id property to “IntegerValue”. They’re not as bad as the Autodesk Vault team with the seemingly meaningless renames, but I definitely have had cause to grumble here.

Changes to the Element.ObjectType mechanism

Prior to 2011, if you wanted to interrogate the type of a given element, you navigated via the Element.ObjectType mechanism and voila, you were at the type element.

In 2011, this mechanism has been deprecated. In its place, you actually have to call a couple of methods to do the same thing:

Element.GetTypeId()   - which returns an ElementId, not an element. You’ll still have to call Document.get_Element() to get the element object.

Correspondingly – whereas you could previously just modify the element type through the property, you now much do Element.ChangeTypeId().

Further, if you used to use “Element.SimilarObjectTypes” to browse the corresponding other types that could be swapped to – you’ll now have to call Element.GetValidTypes().

Replacement of the Analytical Model classes

I have to say I’m relatively unfamiliar with AnalyticalModels – but suffice it to say it’s all been ripped out and replaced with something better! Unfortunately, you’ll have to re-work any existing usages.

Deprecated Events

If you were lazy last time – and didn’t remove your 2009-era events like Application.OnDocumentSaved, OnDocumentSavedAs, OnDocumentOpened, OnDocumentClosed, OnDocumentNewed, OnDialogBox, Document.OnSaveAs, OnSave, OnClose…

Then you’ve got some work to do – because they’ve all been removed now. They’ve all been replaced with newer and better events, though.

New Selection Mechanisms

The existing selection mechanisms (PickOne/Window Select). Find out all about the new mechanisms here.

Ribbon API changes

The ribbon API has had some non-trivial changes… if you’ve done a ribbon panel, it will require updates. See more here.

Print API changes

The PrintSetup.CurrentPrintSetting has been changed to a new interface: IPrintSetting. This should make things better going forward, but again – it will make for a little bit of cleanup time.

Re-work of the gbXML class

If you were previously getting at the gbXMLelement class, there’s some work required to get at its replacement – the EnergyDataSettings class. While it does inherit from Element, it’s been my experience so far that it is not retrievable in the same ways as in the past – it is not a parameter within the ProjectInformation class. Also, it is not queryable in the same ways as before.

To retrieve it in 2011, you need to actually call a static methods:

EnergyDataSettings.GetFromDocument(myDoc)

Also note the other static method: EnergyDataSettings.IsDocumentUsingEnergyDataAnalyticalModel(myDoc) – which tells you if there is an analytical model as well.

 

Installer Changes

While only a minimal installer change is necessary for 2011 (if you still like the bad-old-way of looking through registry uninstall keys to find which versions of Revit are installed and where) – you should probably think about going further.

While the Revit.INI file approach still works – the AddIn file mechanism is better, and the redistributable AddInUtility.dll can really help make this an easier process.

Whew!

While there’s a lot in this document, don’t despair… Chances are your application doesn’t use ALL of these features – does it? There are a variety of common, painful ones – but it’s mostly just mindless grinding through errors.

Just go into it with the right expectations – it will take you at least 3-5x as long as it used to take you with previous upgrade projects! It will be worth it to get to Revit 2011!

No comments: