Access Deleted Element

Here is a

question
posted
by Kristian (Krispy5) Parsons, Systems Analyst – Design at

Westfield Design & Construction Pty. Ltd.
on
accessing deleted element data in the DocumentChanged event handler:

Question: How do I find the category of the deleted elements?
When I try to get the Element from the ElementId it fails since the element has already been deleted.

In my case I would like to warn users when they have deleted a floor.

Answer: I was planning to test your assertion by looking at the Revit SDK ChangesMonitor sample which demonstrates the use of the DocumentChanged method.

I assume that your question is based on using that event to be notified of the deleted elements. Looking at the ChangesMonitor DocumentChanged event handler, I note that it calls a helper method AddChangeInfoRow to list the added, deleted and modified element information in its modeless form. That method includes the following statements and comments which clearly tell you that the deleted element information is no longer accessible:


private void AddChangeInfoRow(
  ElementId id,
  Document doc,
  string changeType )
{
  // retrieve the changed element
  Element elem = doc.get_Element( id );
 
  DataRow newRow = m_ChangesInfoTable.NewRow();
 
  // set the relative information of 
  // this event into the table.
 
  if( elem == null )
  {
    // this branch is for deleted element due 
    // to the deleted element cannot be retrieved 
    // from the document.
 
    newRow["ChangeType"] = changeType;
    newRow["Id"] = id.IntegerValue.ToString();
    newRow["Name"] = "";
    newRow["Category"] = "";
    newRow["Document"] = "";
  }
  else
  {
    newRow["ChangeType"] = changeType;
    newRow["Id"] = id.IntegerValue.ToString();
    newRow["Name"] = elem.Name;
 
    newRow["Category"] = (null == elem.Category)
      ? "<null>"
      : elem.Category.Name; // added by jeremy
 
    newRow["Document"] = doc.Title;
  }
  m_ChangesInfoTable.Rows.Add( newRow );
}

So the answer to your question is presumably ‘no way’, and that is as designed.

There is an obvious workaround, though:

The DocumentChanged event is a very simple notification after the fact. The transaction in which the document was modified has already been closed, you cannot do anything more about it yourself, and as we have seen, you cannot even query the deleted elements for their data.

If you wish to access the document before the transaction is closed, there is a very powerful alternative possibility, the dynamic model update framework or DMU. It enables you to register an updater to be triggered by certain events, and also to react on these events within the same transaction that triggered them.
It is demonstrated by the

DynamicModelUpdate and DistanceToSurfaces SDK samples
and
Saikat’s

Structural DMU sample
.

In your case, you can easily implement an updater that simply brings up a message box as described above, and register a trigger for it which reacts to the deletion of floors and nothing else.
<!–

Your updater Execute method will be called while the transaction is still open, and you can analyse and even modify the elements being deleted before the transaction is terminated.
–>

Your updater Execute method will be called while the transaction is still open.
Unfortunately, even though the transaction is open, you can no longer access or modify the elements that have already been deleted.

The only information available to you at this point is the element id of the deleted element.
If you are interested in floors only, you can maintain a list of all floor element ids and ensure that it is kept up to date using either DMU or the DocumentChanged event.

Response: Yes that all looks good.
By the way, your assumption is correct that I was using the DocumentChanged event.

I looked into the ‘dynamic model update framework’.
This sounds like what I want.

Later: Thanks again, this solved my problem and was easy to implement.

I filter for floors and use the ‘GetChangeTypeElementDeletion’ in the ‘AddTrigger’ call, then in my ‘IUpdater’ class in the ‘Execute’ method I simply display a task dialog showing the number of floors being deleted.
There is no need for any more filtering, I can just use ‘data.GetDeletedElementIds().Count’.


Comments

10 responses to “Access Deleted Element”

  1. Hello, Jeremy.
    You write “updater Execute method will be called while the transaction is still open, and you can analyse and even modify the elements being deleted before the transaction is terminated”.
    As I understand it ‘s possible to get element info (element parameters for example) before they will be deleted or even cancel deleting, i.e. rollback transaction using DMU.
    Can you give some example how to do that?
    What I want.. I need when user delete some elements in model check existence this elements in external database (MS SQL). If elements exist in database ask user for confirmation. If user confirm this operation delete those elements from external database and then delete from model. If exception are throws during deleting from database or user say “No” in confirmation dialog need to rollback transaction.
    Is it possible?
    I’ve tried to do that.
    First of all I’ve created ElementsDeleteUpdater class implements IUpdater interface. Code of this class: http://pastebin.com/61r4yvGx
    In Execute method of this class I want to get Element.UniqueId of each deleted elements:
    var doc = data.GetDocument();
    var deletedElementIds = data.GetDeletedElementIds();
    foreach (var deletedElementId in deletedElementIds)
    {
    var el = doc.get_Element(deletedElementId);
    }
    but el always null. Why? Transaction is still open
    And my code for register updater and trigger
    http://pastebin.com/dk2JFcJc
    Hope to your help.
    Best regards, Victor.

  2. Dear Виктор,
    I am very sorry to say that the statement you refer to is in error.
    There is no way to access an element or its data once it has been deleted.
    If you need any information about it after it has been deleted, you need to somehow store it somewhere else before the deletion takes place.
    Here is another older mention of this fact, in the comments on
    http://thebuildingcoder.typepad.com/blog/2010/10/power-to-the-user-and-application.html
    http://thebuildingcoder.typepad.com/blog/2010/10/power-to-the-user-and-application.html?cid=6a00e553e1689788330133f5511cc1970b#comment-6a00e553e1689788330133f5511cc1970b
    Thank you for bringing up the issue, I corrected the statement above and hope it is clearer now.
    Sorry for the misunderstanding!
    Cheers, Jeremy.

  3. Thanks for quick answer, Jeremy.
    It’s a bad news for me:(
    I want to use another way to resolve my problem. Actually I store ElementUniqueId in my external database. And for deleting element from database I need UniqueId.
    So, I think, if I I have ElementId (in UpdaterData.GetDeletedElementIds Method) may be there is a way to get Element.UniqueId from ElementId.
    I’ve found and read your article about UniqueId, DWF and IFC GUID http://thebuildingcoder.typepad.com/blog/2009/02/uniqueid-dwf-and-ifc-guid.html and wrote function:
    private String GetElementUniqueIdFromElementId(Document document, ElementId elementId)
    {
    if (elementId == null) throw new ArgumentNullException(“elementId”);
    var exportId = ExportUtils.GetExportId(document, elementId);
    int last32Bits = Int32.Parse(exportId.ToString().Substring(28), NumberStyles.AllowHexSpecifier);
    int xor = last32Bits ^ elementId.IntegerValue;
    var sb = new StringBuilder();
    sb.Append(exportId.ToString().Substring(0, 28));
    sb.Append(xor.ToString(“x8”));
    sb.Append(“-“);
    sb.Append(elementId.IntegerValue.ToString(“x8”));
    return sb.ToString();
    }
    http://pastebin.com/bwzqbHBn
    Jeremy, is this correct function for transformation ElementId to UniqueId? I test my function in model with a lot of elements (226000 elements). Function works well.
    Using this way I can deleted elements from external database.
    But I still doesn’t know how to cancel transaction. I wondered, if exception is throw in Execute method, transaction was rolledback. But user receive many error messages.:(
    I hope that in next Revit version we’ll see Cancellable DocumentChanging Event that will be occurs before transaction started.

  4. Dear Виктор,
    I do not believe that you can cancel the transaction, so you will probably have to find some alternative approach to solve your needs.
    If you really want to see this functionality in a future release, you need to make sure this is logged as a wish list item.
    Your function looks fine to me, and I am happy you tested it thoroughly.
    Cheers, Jeremy.

  5. Dear Jeremy,
    I’ve tested to cancel transaction again.
    Here is the Execute Method of my Updater
    http://pastebin.com/u34WiM9A
    Then user push “No, no…” exception is throw. In this case user will see warning message: “Third party updater has performed an invalid operation”. If user choose “Cancel” transaction will rolled back. You can see it if you subscribe to DocumentChanged Event.
    That situation is described in Developer Guide chapter 25.4.2.
    As a result I’ve resolved problem of transaction. But I still didn’t know how to get UniqueId of deleted element. My function doesn’t work when ElementId does not exist in document:( I checked it at first on non deleted elements. I ask a question about it this post http://thebuildingcoder.typepad.com/blog/2009/02/uniqueid-dwf-and-ifc-guid.html#comment-6a00e553e16897883301543571a3bb970c
    Now I think to store UniqueId somewhere when document opening and update it then adding element.
    For example in Dictionary, where ElementId is a Id of each element in model and String is a UniqueId of this element.
    But I don’t like it, because it will work slow on model with 260000 elements )
    Best regards, Victor

  6. Dear Виктор,
    Thank you for the update and glad to hear you solved the transaction issue.
    Your solution sounds perfect to me, and is probably the only way to go at the current time.
    I trust that you will be able to optimise it so that performance is not impacted too much.
    I saw your other question and am discussing it with my colleagues.
    Cheers, Jeremy.

  7. Hello, Jeremy. Thanks for your answers.
    I want to say you that I’ve found more corrected and more safe method for rollback transaction in IUpdater.Execute method.
    I found Document.PostFailure Method. User also will see a message but without exception.
    First of all we must create your own FailureDefinition in OnStartUp Event: http://pastebin.com/gQLi2CLv
    Next, I upgraded Execute Method of my Updater. http://pastebin.com/0XuvD0iW
    I think it is more correct then throw exception.
    May be it’s possible doesn’t show error message using other FailureXXX classes and their methods, but I didn’t learn yet all of them.
    Jeremy, may be you’ll write a thread about cancelation transaction? ;)
    Best Regards, Victor.

  8. Dear Виктор,
    I would love to write such a thread.
    Then I might understand the entire situation better as well :-)
    Would you like to edit a draft for such a post yourself to start with?
    If you summarise your original needs, the experiences made, the problems encountered, the research, workarounds and solutions, I can happily work together with you on editing and getting it posted.
    Basically, you have already stated all the important points, I think.
    Still, it is probably better if you summarise them and see whether anything comes to mind to add before I start working on it as well.
    What do you think?
    Cheers, Jeremy.

  9. Hello, Jeremy.
    Thank you for you trust :)
    My English not so good, but I think I’ll try to write a thread about deleting elements and cancel transaction. But little bit later when I will have finished my current work.
    Best regards, Victor.

  10. Dear Victor,
    My pleasure, and of course I have trust, after the good comments you posted and solutions you came up with.
    Good luck with your work, and I look forward to see what we end up with! I’ll happily clean up any problems with the language, and we can pass it back and forth a few times for the sake of clarity, completeness, and ultimate perfection :-)
    Cheers, Jeremy.

Leave a Reply to Jeremy TammikCancel reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading