Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

DocumentChanged and TransactionGroupRolledBack problem

8 REPLIES 8
Reply
Message 1 of 9
ssdea
595 Views, 8 Replies

DocumentChanged and TransactionGroupRolledBack problem

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():

i1.png

 

This is correct. Modified ids contain the id of the ProjectInfo that was modified.

The second message box is shown after trGroup.RollBack():

i2.png

 

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:

  1. The first idea I have is to record transaction names with affected element ids when the transaction group is started. I couldn’t find anything useful event or flag that indicates that the group transaction is started in Revit API. Could you suggest any API that could help me?
  2. If there is no such API, I should record all the transactions to be able to recall what elements were affected by the group rollback. I can’t hold infinite list in memory. Thus, I need to record some quantity of transactions. The question is what is the maximum number of transactions I should record in this case?

Reproduces on Revit 2022, 2023, and 2024.

 

8 REPLIES 8
Message 2 of 9
jeremy_tammik
in reply to: ssdea

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:

  

https://stackoverflow.com/questions/62224410/revit-api-how-to-check-for-open-transactions-sub-transa...

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 3 of 9
ssdea
in reply to: jeremy_tammik

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():

 

i3.png

 

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:

  1. Flag in Revit API that indicates that transaction group (not transaction) is started.
  2. Implement my own TransactionRecorder that will record all transaction names and IDs received by DocumentChanged handler. When the transaction group is rollbacked, DocumentChanged is raised with event args containing transaction names that were rollbacked. Thus, I can find (theoretically) which IDs were rollbacked. However, this will require to record some number of transactions. Is there a constant in Revit that indicates the maximum number of transactions in a transaction group?

Could you suggest anything for the workarounds above, please?

Message 4 of 9
jeremy_tammik
in reply to: ssdea

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!

  

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 5 of 9
ricaun
in reply to: ssdea

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.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 6 of 9
ssdea
in reply to: jeremy_tammik

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:

  1. Open “TestAddin.sln” in VS.
  2. Build the project TestAddin.
  3. Copy “TestAddin.dll” and “TestAddin.addin” from “TestAddin\bin\Debug” to “%APPDATA%\Autodesk\Revit\Addins\2024”.
  4. Launch Revit.
  5. Make sure that the add-in is loaded.
  6. Open any Revit project or create a new one.
  7. There should be a new tab “TEST TAB” on the ribbon.
  8. Click “Test” command.
  9. Two message boxes should appear one after another:
    1. The first one indicates that one element was modified. It appears after firstTransaction.Commit() and contains data from DocumentChangedEventArgs received.

      i1.png

    2. The second message box indicates that DocumentChanged was fired after trGroup.RollBack(). The problem is it doesn’t contain any modified ids. This is the case when my application gets unsynchronized. In my opinion, this is incorrect.

i2.png

 

This behavior can be reproduced in Revit 2022, 2023 and 2024. The zip archive contains “RevitAPI.dll” and “RevitAPIUI.dll” from Revit 2024.

 

Message 7 of 9
ssdea
in reply to: ricaun

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.

Message 8 of 9
ricaun
in reply to: ssdea

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.

 

CommandTransactionGroupProblem.PNG

 

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.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Message 9 of 9
ricaun
in reply to: ssdea

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.

 

TransactionGroupRolledBack - 2024-02-14 13-46-28.gif

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Forma Design Contest


Rail Community