I'm implementing Revit add-in that monitors how user modifies Revit document. My code relies on ControlledApplication.DocumentChanged event to act in correspondence with added, modified, and deleted elements. Unfortunately, I found the scenario when elements are changed, but DocumentChangedEventArgs doesn't contain any added, modified, or deleted element ids.
At first, I noticed such a scenario in the Manage Links window. After a while, I was able to implement the code that reproduces this problem.
I have a command that creates transaction group with single transaction in it. Note, that there is transaction group rollback in the end. The code is the following:
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
var doc = commandData.Application.ActiveUIDocument.Document;
using (var trGroup = new TransactionGroup(doc))
{
trGroup.Start("Test group");
using (var firstTransaction = new Transaction(doc))
{
firstTransaction.Start("Test transaction");
doc.ProjectInformation.Name = "Test Value";
firstTransaction.Commit();
}
trGroup.RollBack();
}
return Result.Succeeded;
}
Also, I have DocumentChanged event handler that shows message box with data from the event args:
private void ControlledApplication_DocumentChanged(object sender, DocumentChangedEventArgs e)
{
var sb = new StringBuilder();
e.GetTransactionNames().ToList().ForEach(t => sb.AppendLine(t));
MessageBox.Show($"Added ids count: {e.GetAddedElementIds().Count}, Modified ids count: {e.GetModifiedElementIds().Count}, " +
$"Deleted ids count: {e.GetDeletedElementIds().Count}, Operation: {e.Operation}, Transactions: {sb}", "DocumentChanged");
}
When the command is executed, 2 message boxes appear one after another:
The first message box is shown after firstTransaction.Commit():
This is correct. Modified ids contain the id of the ProjectInfo that was modified.
The second message box is shown after trGroup.RollBack():
Note, that after the group rollback DocumentChanged receives no element ids. Expected behavior is that there should be element id of the element that was rollbacked. This is critical to my application because it gets unsynchronized with the document. Thus, I have the following questions:
Reproduces on Revit 2022, 2023, and 2024.
Sorry, but I think the behaviour you describe is exactly as intended.
The interior transaction is completely irrelevant as long as it is wrapped in a transaction group and the latter is rolled back.
The call to DocumentChanged made before the transaction group is committed (which it never is) is also invalid.
You could implement code to flag your open transaction group and use that to postpone processing of all DocumentChanged information until after the group has been successfully committed.
Here is a discussion of how to check for open Transactions, Sub-transactions, or Group Transactions that might come in handy for such purposes:
Thanks for the quick response.
The idea of flagging that the transaction group has started would work if my add-in was the only source of transaction groups in Revit. The transaction group may be started by any command in Revit (like Manage Links) or external add-ins. My add-in will not know that the transaction group has started in such cases.
Also, when the transaction group is committed (or assimilated) the DocumentChanged is not raised at all. I replaced trGroup.RollBack() with trGroup.Commit(). When the command is executed, I see only one message box after firstTransaction.Commit():
No message boxes after trGroup.Commit().
IsModifiable property doesn’t work for me, because Revit sets it to true only when the transaction is started, not the transaction group.
Thus, I require one of the following workarounds:
Could you suggest anything for the workarounds above, please?
OK, I can see how that can be problematic and unnecessarily hard to handle or work around. I would like to discuss this in more detail with the development team. However, in order for them to be able to take a look at all and analyse the behaviour in depth, they will require a complete and minimal reproducible case. Can you please supply that? Thank you!
I had the same problem last year, after noticing the rollback group with no ID in the event DocumentChanged.
I'm using the Idling event to know if the TransactionGroup is assimilated, the Idling event is not gonna trigger inside a Transaction/TransactionGroup, only the DocumentChanged event gonna trigger.
After some DocumentChanged events without the TransactionGroupRollback, an Idling happened means all the DocumentChanged was committed without a group rollback.
In my case, I didn't create a TransactionRecorder yet. But you could record all the DocumentChanged transactions and in the Idling clear the list. If a TransactionGroupRollback happens you could roll back each transaction in the TransactionRecorder until matches the transactions in the TransactionGroupRollback event.
A way to check if the TransactionGroup is open would be nicer, to know when to start recording the transactions.
I'm implementing Revit add-in that monitors how user modifies Revit document. My code relies on ControlledApplication.DocumentChanged event to act in correspondence with added, modified, and deleted elements.
My add-in work well for cases when modifications are committed by regular Transactions. I’m facing a problem for scenarios related to TransactionGroup. When the TransactionGroup is rolled back with trGroup.RollBack(), DocumentChangedEventArgs doesn't contain any added, modified, or deleted element ids. I was expecting that event args would contain rolled back element ids, because the rollback changes the elements state which should be processed by my add-in. Thus, my add-in gets unsynchronized with elements state when a transaction group is rolled back. In case when transaction group is committed (or assimilated), my add-in is fine, because the event args sent after inner transactions commitment are valid.
This behavior can be reproduced in any Revit project. Just create a new project from any template.
The VS solution can be found in zip attached. Steps to launch the sample add-in:
This behavior can be reproduced in Revit 2022, 2023 and 2024. The zip archive contains “RevitAPI.dll” and “RevitAPIUI.dll” from Revit 2024.
Thanks for great idea.
I tested Idling event with Manage Links window. When the window appears, Idling stops being raised. This will simplify transactions recording for me, if there is no other way.
The TransactionRecorder may not work 😐
I was messing with TransactionGroup and found out that is possible to start a TransactionGroup inside a TransactionGroup. In the TransactionGroup the method called Assimilate, this method merges all the transactions committed inside the group in one single undo item.
Here is the setup, open a TransactionGroup called 'Rollback', then open another TransactionGroup called 'Assimilate', then do some Transactions and commit in the document, assimilate and rollback.
In the end, nothing gonna change in the document, but the event DocumentChanged gonna show the event TransactionGroupRolledBack with no elements and with the transaction name 'Assimilate'.
How I can figure out what was inside this 'Assimilate', the DocumentChanged event never shows that all the transactions were merged into a new one.
Here is a full sample of the problem, that creates two transactions assimilates them, and rollback everything. The code records and shows the DocumentChanged events.
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.UI;
using System;
using System.Text;
namespace RevitAddin.Commands
{
[Transaction(TransactionMode.Manual)]
public class CommandTransactionGroupProblem : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elementSet)
{
UIApplication uiapp = commandData.Application;
Document document = uiapp.ActiveUIDocument.Document;
uiapp.Application.DocumentChanged += Application_DocumentChanged;
try
{
using (TransactionGroup transactionGroupMaster = new TransactionGroup(document))
{
transactionGroupMaster.Start("RollBack");
using (TransactionGroup transactionGroup = new TransactionGroup(document))
{
transactionGroup.Start("Assimilate");
using (Transaction transaction = new Transaction(document))
{
transaction.Start("Author");
document.ProjectInformation.Author = $"{DateTime.Now}";
transaction.Commit(); // TransactionCommitted Author
}
using (Transaction transaction = new Transaction(document))
{
transaction.Start("ClientName");
document.ProjectInformation.ClientName = $"{DateTime.Now}";
transaction.Commit(); // TransactionCommitted ClientName
}
transactionGroup.Assimilate(); // No event
}
transactionGroupMaster.RollBack(); // TransactionGroupRolledBack Assimilate
}
}
finally
{
uiapp.Application.DocumentChanged -= Application_DocumentChanged;
}
TaskDialog.Show("TransactionGroupProblem", DocumentChangedString.ToString());
return Result.Succeeded;
}
StringBuilder DocumentChangedString = new StringBuilder();
private void Application_DocumentChanged(object sender, DocumentChangedEventArgs e)
{
DocumentChangedString.Append($"{e.Operation} \t {string.Join(" ", e.GetTransactionNames())}\n");
}
}
}
I have no idea how to figure out what is inside the 'Assimilate'.
You could edit the title of this post with something like: DocumentChanged and TransactionGroupRolledBack problem.
Looks like Revit 'Project Browser' does not update well when using TransactionGroup with Rollback, after changing the name of the View the rollback does not update in the 'Project Browser'. After closing and opening everything refreshes.
Can't find what you're looking for? Ask the community or share your knowledge.