Family Category, Element Ids, Transaction and Updates

Lots of topics to discuss, all from Revit API discussion forum threads or GitHub updates:

Family Category

Recently, several people encountered issues retrieving the category of a family.

This is a known issue.

The Element.Category property is often not implemented for family definitions.

The simplest solution is to use the Family.FamilyCategory property instead.

Another solution is to retrieve any of the symbols – also know as types – defined by the family and ask it for its category instead.

Here are two threads discussing related issues:

The first one answers this very question and points to several example solution implementations.

Keeping Track of Elements

Cwaluga raised a thread to discuss the general workflow for keeping track of elements, and Arnošt Löbel very kindly answered in depth:

Question: I am not sure what is the best way to accomplish what I intend to do.

I am working completely integrated in Revit, so no import/export/worksharing issues for the moment.

However, I have to keep track of the elements.

Now if I understand correctly, it is discouraged to rely on Element IDs, since those may not be unique in linked scenarios and may even change within a document. Instead it is recommended to use Unique IDs for this, but the API makes working with these not easy. Moreover, if I want to obtain an Element from an ID, I have to know the document as well. I could now always check all documents if they contain an element or always store a reference to the document alongside with the ID. I could however also work directly with the elements. They know which document they belong to and in case I need something from the element, I do not have to create one from the ID first. However, if I recall correctly, I read some time ago that this may be inefficient. Is that so?

To put it in one short question: Is it all right to excessively keep references of elements around? If not: why? And is there a better way?

Answer: Keep an eye on the trends set by the Revit API implementation itself.

It is consistently moving away from passing elements as arguments, and using element ids instead.

Therefore, I would agree that what you are doing is best:

Response: Thanks so much for your suggestions, Jeremy. I'll try to avoid keeping the elements alive more than necessary. But there are some points revolving around this, which are not obvious from the documentation.

If I may add two short follow-up questions:

Arnošt replies: I'd say it's fairly all right to store and work with Elements directly, in the public managed API anyway. We've made quite an effort to make element objects stable no matter how (and if) the model from which the elements are changes. The same does not apply internally in Revit native code where we need to be more cautious of memory re-allocation, which is why, internally, we generally prefer working with Ids. That general approach mirrors in the public API, for our goal is to have a public API that is as close to our internal API as possible.

So, my general recommendation would be:

There is one point of caution: I've stated that we had made elements quite stable. That is certainly true for most kinds of element, but not for all, unfortunately. There are (I believe) still a few older kinds of elements that do not yet have an “ironclad” wrapper around them. Basically, if an element class does not have the IsValidObject method in their class, those elements would not be safe to hold on to, because in case the actual elements are deleted (or undone, or redone, of if their document got closed), the managed object would not know about it and any operation performed on it would lead to a crash. So, if you store elements make sure it's the kind that implements the IsValidObject method, and grow the habit of always testing that method before every use of the element.

As for performance impact, there are, again, a few points I'd like to make:

Oh, and to answer some of the questions Cwaluga asked:

Undoing a Transaction

Troy raised another interesting question on undoing a transaction, in his thread on transactions and document events.

Question: I am trying to figure out a way to undo a previous transaction that I have committed from a future transaction.

Backstory: I am doing some stuff during the DocumentPrintingEventArgs event and then allowing the print to happen. Then I am reverting all the stuff I did after the print using the DocumentPrintedEventArgs event as I don't want the changes to be permanent.

This is taking a lot of code to do, plus the larger/more complex the model, the longer it takes the code to run. I was wondering if I could roll back the previous transaction that happened during the PrintingEvent from the PrintedEvent? Or is it possible for a transaction to span these two events?

Answer: The only possibility that I know of that remotely approaches what you are asking for is to wrap all of your transactions in a transaction group, commit the transactions that you need to commit, and then roll back the entire transaction group without committing it.

However, I do not know whether it is possible to launch a call to print within an open transaction group. I suspect not.

Here are two explanations of transactions, sub-transactions, transaction groups and using transaction groups.

That covers about all that you can achieve in this area programmatically.

If you really have to launch the print command, and that cannot be achieved within your own open transaction group, then I guess you would have to take recourse to the Revit Undo command instead, to roll back the already committed transactions.

I do not know whether the Undo command can be driven programmatically, e.g. using the PostCommand method.

If all else fails, how about saving your document before printing it, creating a copy for the complicated printing process you describe, and then reverting back to the original document once that is done?

Please KISS, always and everywhere!

Arnošt replies: I am afraid there is no solution to what you want to do. There is no tie between the pair of events (pre and post printing) and any transactions that happen during either of the events are unrelated.

Revit API does not allow undoing transactions for a reason: mainly to avoid external application to block each other. However, even if undoing was allowed, it is unlikely to help in cases like yours, because you can never be sure that your transaction was the latest finished and it is impossible to undo transaction that are further down the undo stack (without undoing later transactions, of course). There may be number of application subscribed to printing events, and it may be different applications subscribed to each of the two events. Any of the applications in any of the events can have multiple transactions that they would not like to be redone by some other application. There is simply no way around that problem.

Jeremy mentioned using transaction groups to wrap your transactions in, but that is not an option in this case either. For your transactions happen during an event (not a command) we do not allow any even handler to leave any transaction-related scope (such a transaction, transaction group, etc.) open upon returning from the handler. If Revit sees such a scope left open, it forces it to close and then deletes everything that the handler has done to the model.

Like I said, there is really nothing that can be done. It's not like we haven't thought about it, but considering all the implications we cannot provide a save and stable solution.

RevitLookup Update

Maxence Delannoy and BobbyCJones discovered an exception in RevitLookup due to the GetAnalyticalModel property returning null in RAC and MEP.

Maxence fixed it and also removed the unused default constructor in the GitHub pull request GetAnalyticalModel returns null #10.

Here are the modifications he made.

I integrated Maxence's enhancements into the RevitLookup master branch in release 2016.0.0.10.

The Revision API and GetRevisionData Update

The following query on how to draw a revision cloud prompted me to update the GetRevisionData add-in:

Question: I would like to develop a Revit add-in providing functionality similar to Revit's built-in revision cloud with some custom stuff added. Does the Revit API provide the facility to draw revision clouds, or to drawing sketches of them with our custom operator?

Answer: An entire new Revision API was implemented in Revit 2015, so What's New in the Revit 2015 API section on the Revision API covers just about all there is to know.

You may also be interested in the GetRevisionData Revit add-in.

I just upgraded the GetRevisionData GitHub repository to Revit 2016 for you, in release 2016.0.0.1.

ADN Revit API Training Labs Update

The ADN Revit API Training material HelloWorld module in C# and(VB both implement the HelloWorldApp external command.

Among other things, it demonstrates the result of returning Result.Failed, which causes Revit to display an error message to the user and highlight the returned elements graphically – presumably to show the user that they are the ones causing an issue.

This command was returning Result.Succeeded instead, so I fixed that and published it in release 2016.0.0.9.

At the same time, I also synchronised the official ADN Revit API Training material with my ADN Revit API Xtra labs, publishing release 2016.0.0.9 of those as well.