Prompt Cancel Throws Exception in Revit 2018

I just picked up an ADN case on a topic that was already raised yesterday in the Revit API discussion forum thread on Revit 2018 API undocumented changes, so it is definitely worth highlighting here as well:

Question

A bug may have been introduced into the Revit 2018 API UIDocument PromptForFamilyInstancePlacement method.

In Revit 2017, hitting the Escape key twice after placing the families would end the command.

In Revit 2018, hitting the Escape key twice generates an OperationCanceledException and all the elements that were just placed are deleted.

The problem can be observed by comparing the behaviour of the Revit 2017 SDK PlacementOptions sample add-in with the Revit 2018 SDK version of the PlacementOptions sample add-in.

Change in Behaviour

Matt Taylor describes the situation differently in his Revit API discussion forum thread on Revit 2018 API undocumented changes:

Each year I upgrade my codebase for use with the new version of Revit. Each year, I rid my code of deprecated and/or obsolete function warnings/errors.

Each year I seem to find an undocumented change in the way the Revit API works.

'The Factory', can we please have a more detailed and complete list of changes? Can you add this change to the documentation, please?

My 'find' this year is a change in the way PromptForFamilyInstancePlacement works.

This function used to just return focus to your function upon cancelling by the Reviteer.

In Revit 2018, cancelling of this function by your Reviteers throws an Exceptions.OperationCanceledException [sic] exception.

Easily fixed, once discovered:

Try
  docUi.PromptForFamilyInstancePlacement(FamilySymbol)
Catch ex As Exceptions.OperationCanceledException
  ' The user cancelled placement.
  ' This should only trigger in Revit 2018.
  ' Do something if you like
End Try

This change even makes sense!

It's a good idea!

It also fills me with dread. What else is going to throw an exception unexpectedly?

What other changes are there?

(Yes, I know that this item is vaguely alluded to in the 'what's new' document, but it's not documented anywhere.)

Have you found any hidden 'treasures' that you want to share?

Exceptions Should be Exceptional

Greg 'Sherbs' adds a very valid additional point:

Yikes!

Good catch!

This is more than a bit concerning.

Undocumented exceptions are generally going to be application fatal.

I hope this sort of thing can be addressed more systematically in upcoming releases.

Regarding the specific find, my opinion differs:

There really is nothing 'exceptional' or 'unexpected' here.

Cancelling placement may be infrequent, but it is an entirely normal user action.

Why even throw in this case at all?

I'm not terribly passionate about this, just throwing out another viewpoint.

I'm a bit of minimalist when it comes to the use of exceptions.

Interrupt Process

By Stephen Charles ThompsonOwn work, CC BY-SA 3.0, Link

Answer

Many thanks to Matt for pointing this out!

I would say that this change in behaviour is precisely alluded to, not vaguely, in the documentation of What's New in the Revit 2018 API section on UIDocument.PromptForFamilyInstancePlacement() behavioral change:

The behavior for UIDocument.PromptForFamilyInstancePlacement() was changed to be same as that of PickObject() methods...

Raising the exception you mention corresponds exactly to the PickPoint behaviour.

However, just as you say, the detailed consequences are not explicitly spelled out.

I also fully agree with Greg's statement: exceptions should be exceptional.

Expected behaviour should not be communicated using exceptions.

I have been preaching this for years to little avail:

To answer the original question raised above: You need to catch and handle (or ignore) the OperationCanceledException as shown by Matt.

If you code does not, your transaction will presumably not be committed.

The family instances that were successfully placed before the user cancelled the placement and the exception was thrown are probably removed as the transaction is rolled back.

No bug, just a change in behaviour.

I hope this clarifies and all is now illuminated.

The Building Coder Samples CmdPlaceFamilyInstance

I implemented the external command CmdPlaceFamilyInstance in The Building Coder samples to exercise the PromptForFamilyInstancePlacement method when it was originally introduced.

It also includes code using the DocumentChanged event to retrieve the newly created elements.

I updated it to handle the OperationCanceledException as shown by Matt in release 2018.0.132.2.

Here is the diff to the preceding release that shows exactly what modifications were made; simply add an exception handler around the call to PromptForFamilyInstancePlacement:

  try
  {
    uidoc.PromptForFamilyInstancePlacement( symbol );
  }
  catch( Autodesk.Revit.Exceptions.OperationCanceledException ex )
  {
    Debug.Print( ex.Message );
  }

The Exception