Hello all,
does anyone know of a supported (work sharing safe) way to obtain the UniqueId of a deleted element within a custom dynamic updater's Execute() method?
You get a list of ElementIds which is nice, but the elements can not be retrieved using Document.GetElement(), presumably because they have already been deleted from the document before the updater is called. The only way I know to obtain the UniqueId is directly from the Element, which I don't have. I need to match the deleted elements against a list of UniqueId strings that I have stored previously. I'll settle for unsupported hacks if need be at this stage, but I want to play nice with work sharing if possible.
UniqueIds are great for most things, but seem to be completely useless in this situation.
Any ideas?
Feature request: Failing a solution to the above, it would be nice if a pair of methods could be added to the DB.Document class that would allow directly converting ElementIds to UniqueId strings and vice versa (and would behave nicely for deleted elements within the updater call).
Call them something like:
Alternatively, providing a list of UniqueIds along with the ElementIds to the UpdaterData object would be handy.
Solved! Go to Solution.
Solved by jeremytammik. Go to Solution.
I know this is not a solution that we all hoped for, but since half a decade has passed and we still don't have this implemented in Revit by the Autodesk team, I thought I do my best to expand on Troy's solution:
First we need to override the "delete" command:
private static void OverrideDelete(UIApplication app) { try { var commandId = RevitCommandId.LookupCommandId("ID_BUTTON_DELETE"); if (commandId == null || !commandId.CanHaveBinding) return; var binding = app.CreateAddInCommandBinding(commandId); binding.Executed += OnDelete; } catch (Exception) { //ignored } }
Once we have that we need a handler for the new method that we will fire up instead:
/// <summary> /// Handler for when user deletes an element from the model. /// </summary> private static void OnDelete(object sender, ExecutedEventArgs e) { try { var deleted = new Dictionary<string, Element>(); var deletedIds = new List<ElementId>(); var selection = new UIDocument(e.ActiveDocument).Selection.GetElementIds(); using (var trans = new Transaction(e.ActiveDocument, "Delete")) { trans.Start(); // (Konrad) First we need to find out what Elements will be deleted if we were to proceed. // Remember that Elements in Revit are intertwined so deleted one, might affect others. // In SubTransaction we can delete them, collected actual affected Ids, and then undo. using (var subTrans = new SubTransaction(e.ActiveDocument)) { subTrans.Start(); if (selection.Any()) { deletedIds = e.ActiveDocument.Delete(selection).ToList(); } subTrans.RollBack(); } // (Konrad) Now that we have actual affected Ids of deleted elements, we can get their UniqueIds // We need these UniqueIds. if (deletedIds.Any()) { foreach (var id in deletedIds) { var el = e.ActiveDocument.GetElement(id); if (el == null) continue; if (!deleted.ContainsKey(el.UniqueId)) deleted.Add(el.UniqueId, el); } } // (Konrad) Finally we actually delete the selected elements so that user doesn't notice that // we have hijacked the Delete method. e.ActiveDocument.Delete(selection); trans.Commit(); } } catch (Exception) { //ignored } }
So this is a little bit more detail on the method that Troy suggested. I have tested it a little, and it works for my use case.
Good luck!
Five years later after the original post, this question is still just as relevant as ever. I'm working in Revit 2020 and still see no additional features that facilitate this process. It seems like an enormous limitation that there is no way to obtain worksharing-safe UniqueIds in a deletion event.
Thoughtful contributors have offered up alternatives, but not all are practical. For those of us who develop commercial applications, it can be a huge risk to intercept the Delete command and modify it. You're basically playing Russian Roulette, hoping that the end user does not have another app attempting to do the same. If I'm not mistaken, the act of intercepting a built-in command is first-come, first served. So if another app on the client machine loads first and grabs the command, you could lose core functionality.
For those who find themselves in this situation, you could try yet another non-ideal solution that I've experimented with. You can periodically iterate over the model and update a running list containing critical data for the "important" elements that you want to track. This can be done either with "on-idling" or with a background thread and external events for greater control over the timing and CPU resources. Then when your IUpdater captures a deletion event, you iterate the running list of tracked elements to see if they match the deleted IDs and reference the crucial data stored in the list.
That being said, I still see this as a hacky workaround that comes with plenty of pitfalls. I would love to see more data available in the UpdaterData for a deletion event. At the very least, UniqueIds are a fundamental cornerstone in event-handling for a worksharing-safe application.
@Anonymous , I'd love to hear your current thoughts on this topic.
6 years later, are there any news on these: "ADN case number 10292440 [Accessing deleted elements] and wish list item CF-2162 [API: DocumentChanging or ElementsDeleting event before element is deleted]"
We really need to find out what exactly was deleted in the OnDocumentChanged event. Having only the ID of the deleted element does not help in any way.
6 years later, - again - are there any news on these: "ADN case number 10292440 [Accessing deleted elements] and wish list item CF-2162 [API: DocumentChanging or ElementsDeleting event before element is deleted]"?
We are running into a situation where we need to obtain the deleted element's Unique Id, so then we can use this Unique Id to find a matching parameter value on an existing element, which is used to link to the deleted element. This way we can clear out this parameter because the linked Unique Id no longer exists.
I wanted to note on the purposed code solution that I attempted to roll something similar to address this issue, but ran in to an issue with TextNotes. Whenever a user uses the backspace key to delete a character in the TextNote, this will work fine as the Backspace key is not captured. If the user uses the Delete key to delete a character in a TextNote, then this event will fire. In the ExecutedEventArgs, there was no way I could identify to capture the location of the mouse when the user pressed the delete key to manipulate the string in the TextNote.
The code then either has two options: return the TextNote without any of the text manipulated (which then the user wonders "what happened? how come the delete key doesn't work anymore?) or by deleting the Element, which in this case is the whole TextNote and not a character in the TextNote (to which the user wonders "why is the whole Text Note being deleted")
As many on this thread have noted, I would personally like the OwnerViewId after an element is deleted in the view to identify if the view is still the way it originally was when the document was opened or if the user has deleted an element and which view the item was deleted. I could get this from ActiveView (which may be my fallback), but let's say that I want to really, really be sure of the Element that was just deleted was from a specific view. Also to note (not that it tremendously matters in this scenario, but I was using DocumentChanged over an Updater.
Wow, that sounds tricky. I passed on your input to the development team.
Hi all,
I too could benefit from the development of `ElementsDeleting` event (read-only) with this information: element id, category (or api class), name (if present).
Even better if there was a updater (write access) with that information.
My use case is this @jeremy_tammik. I maintain logical relationships and custom data between elements (extensible storage & co). When one element is deleted, I must clear the links to the other elements. In some cases, I must delete the dependent elements. For now, I have the very hacky way to do this: I have a mega-map, in memory ID-parent -> ID-children | ID-siblings, and I use the updater sole info (the ID) to check if it's in the map, and the delete|reset the dependent elements.
My biggest problem still remain: the undo (ctrl-Z). It triggers the OnDocumentChanged (read-only) event with a list of constituents (the model-lines and the sketch of a floor, for instance), but not the ID of the element itself! Also, that event is read-only. The undo does not trigger any updater (which is write-access, and would be useful to me).
I am pretty sure there is a wish list item for this in the Revit Idea Station. Please vote for that and add your business case. Thank you!
Hi All,
I would also benefit from an updater OnElementDeleting, and I would like this information: id, category (or api class) and name (if exists). Read-only access to all parameters ? Even better.
@jeremy_tammikMy use case is this. I maintain logical relationships between elements via extensible storage & co. Every time an element is deleted, I must clear the xstorage of its dependent elements (this involves a transaction) or delete the depent elements themselves.
For now, I use a very hacky way to do this. I have a mega-map of ID-parent-to-ID-children in memory, built OnDocumentOpened and destroyed OnDocumentClosing events, and I use I use the updater's GetDeletedElementIds() to make my changes the Revit project. Is that undo stack, Arnost was talking about, available to the API ?
I am still left with one major problem. The undo (ctrl-z) command. First, this triggers only the OnDocumentChanged event - which is a read-only context - and no updater. Second, the GetDeletedElementIds() of that event returns the IDs of the element's contituents (for a Floor, it's the sketch model-lines) but not the element's itself (the Floor)!
I passed on your request and use case to the development team. Let's see what they say...
Can't find what you're looking for? Ask the community or share your knowledge.