IUpdater is triggered and run with no errors but model changes are undone

IUpdater is triggered and run with no errors but model changes are undone

amrshaalan
Participant Participant
1,100 Views
9 Replies
Message 1 of 10

IUpdater is triggered and run with no errors but model changes are undone

amrshaalan
Participant
Participant

Salute,

 

IUpdater is triggered and run with no errors but model changes are undone for no obvious reason

 

Heeelp, I'm pulling my hair out since morning.

I'm creating an application that searches the document for Rooms and then uses each Room boundary to create a new floor this is done on my check list.

I'm creating an IUpdater so that if the user moves a wall it changes the rooms boundary and therefore uses the new room boundary to create a new floor and delete the old one, what happens is when i try to move a wall the iupdater is triggered and the code runs with no errors but then i find the wall back to its original location I don't really know why and I can't connect any dots!!!

 

here's my code for the iupdater execute method:

 

try
            {
                foreach (ElementId elemId in data.GetModifiedElementIds())
                {
                    List<Floor> floorsByRoomsList = new List<Floor>();
                    Room r = doc.GetElement(elemId) as Room;
                    if (r != null)
                    {
                        SpatialElementBoundaryOptions sb = new SpatialElementBoundaryOptions();
                        sb.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.CoreBoundary;
                        IList<IList<BoundarySegment>> loops = r.GetBoundarySegments(sb);

                        List<Floor> floorsInDoc = new List<Floor>();
                        floorsInDoc = GetAllFloors(doc);
                        
                        foreach(Floor existFloor in floorsInDoc)
                        {
                            string floorMark = existFloor.get_Parameter(BuiltInParameter.ALL_MODEL_MARK).AsString();
                            if (floorMark == r.Id.ToString())
                            {
                                Floor newFloor = null;
                                Opening newOpening = null;
                                if (loops.Count == 1)
                                {
                                    #region the new floor properties
                                    CurveArray floorprofile = Data.ObtainCurveArray(r);
                                    FloorType fl = existFloor.FloorType;
                                    Level level = r.Level;
                                    bool bl;
                                    if (existFloor.GetAnalyticalModel() == null)
                                        bl = false;
                                    else
                                        bl = true;
                                    #endregion
                                    newFloor = doc.Create.NewFloor(floorprofile, fl, level, bl);
                                    floorsByRoomsList.Add(newFloor);
                                }
                                else if (loops.Count > 1)
                                {
                                    #region the new floor & opening properties
                                    CurveArray floorprofile = Data.ObtainCurveArray(r);
                                    FloorType fl = existFloor.FloorType;
                                    Level level = r.Level;
                                    bool bl;
                                    if (existFloor.GetAnalyticalModel() == null)
                                        bl = false;
                                    else
                                        bl = true;

                                    CurveArray openingProfile = Data.ObtainOpeningCurveArray(r);
                                    bool blO = true;
                                    #endregion
                                    newFloor = doc.Create.NewFloor(floorprofile, fl, level, bl);
                                    floorsByRoomsList.Add(newFloor);
                                    newOpening = doc.Create.NewOpening(newFloor, openingProfile, blO);
                                }
                            } // if the floor is not located inside room and its name is not equal to room id
                            else
                            {
                                continue;
                            }
                        }
                    } // if the modified element is not a room
                    else
                    {
                        continue;
                    }
                    foreach (Floor fl in floorsByRoomsList)
                        fl.get_Parameter(BuiltInParameter.ALL_MODEL_MARK).Set(r.Id.ToString());
                }
            }

 

0 Likes
Accepted solutions (1)
1,101 Views
9 Replies
Replies (9)
Message 2 of 10

jeremytammik
Autodesk
Autodesk

Salute and Happy New Year to you.

 

One possibility I can think of is that once the floor has been created, Revit automatically keeps track of the link between the wall and the room and the floor and updates it all itself automatically.

 

Therefore, maybe your updater interferes with the situation after it has already been fixed.

 

You should probably implement a really easy way to disable your updater, both for testing and for performance, and give the user a choice of whether it should run or not.

 

Other thatn that, I can just suggest more simplification and debugging.

 

Kiss!

 

Good luck, and take care with that hair!

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 3 of 10

amrshaalan
Participant
Participant

Dear Jeremy,

 

giving the user the opportunity to choose wether to activate the automatic updater is on my to do list.

But for now let's say the user chooses to update automatically!

I debugged my Code and it showed no errors at all, and the reason it's so0oo weird is that my code is not dealing with any walls by any means....why does the wall go back to its previous location!!!! what would you do in my case?!

Here's my Pseudo Code for this:

 

 

1-When a floor is created by our application add a parameter value in the floor that matches a parameter value in the room

for instance (FloorMark.ToString() = Room.Id.ToString() )

2-When the updater starts, get all floors in document

3-search for this parameter until you find the a floor meets the condition, if the parmeter matches the room changed then delete the existing floor and create a new one.

 

Cheers,

Amr,

0 Likes
Message 4 of 10

amrshaalan
Participant
Participant
Accepted solution

Dear Jeremy,

 

after various trials and errors i found out that the problem is that revit doesn't update and reflect the changes instantly, so adding the newOpening() method right after the newFloor() method didn't work because someway the floor wasn't still written in the model, so you have to write it manually by adding

doc.regenerate();

between the newFloor() and the newOpening() methods, this will reflect the latest changes to the model and therefore the newOpening method will work nicely.

 

Kind Regards,

Amr,

Message 5 of 10

arnostlobel
Alumni
Alumni

This is a good one and deserves a kudo (which I gave) for sharing with the rest of our API community. Amr is absolutely correct - regeneration is needed in order to use the newly created floor. We always tell all API user to keep in their minds that after changes (or new creations) are made then the model must be regenerated first before the caller can read properties of the modified or created elements. It is because the set changes still needs to be propagated to the rest of the model or even to other parts of the changed elements. And this is exactly what is happening here too, although it is not so obvious, for Amr's application does not explicitly read properties of the newly crated floor, it does put opening it it, which is practically the same thing only disguised,

Arnošt Löbel
Message 6 of 10

amrshaalan
Participant
Participant
Thank you Arnost, it took me sometime to find out that, but glad i eventually did 🙂
Message 7 of 10

jeremytammik
Autodesk
Autodesk

Congratulations!

 

I should add this to the list of discussions on the Need to Regenerate:

 

http://thebuildingcoder.typepad.com/blog/about-the-author.html#5.33

 

Cheers,

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 8 of 10

amrshaalan
Participant
Participant
yes, sure feel free to add it 🙂
cheers,
Amr,
0 Likes
Message 9 of 10

jeremytammik
Autodesk
Autodesk

Dear Amr,

 

Thank you again for your research and sharing this.

 

I published a summary for all to enjoy:

 

http://thebuildingcoder.typepad.com/blog/2016/01/idling-dmu-documentchanged-and-need-for-regen.html#...

 

Cheers, 

 

Jeremy



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 10 of 10

frankloftus
Contributor
Contributor

@amrshaalan, this helped me out. Thank you for sharing the solution!

 

I do have some subsequent questions related to my use case, though (not directed to anyone in particular, just sharing my experience hoping someone can provide explanation).

 

My code, which is working now, thanks to Document.Regenerate(), creates an updater that detects when an electrical equipment instance has it's distribution system changed.

1.If the distribution system is set to 'None', the updater will trigger and show a dialog that prompts the user to select a distribution system from a list.

2. When the user selects, the element's distribution system parameter is set to the distribution system that was selected.

3. Info from the distribution system is written to a parameter of a separate annotation symbol.

 

When this operation is done in the context of a normal transaction, everything works as expected. The distribution system is set, then the info is sent elsewhere, all in the same transaction, without any regeneration or subtransactions needed.

 

However, when I tried to make this happen in an updater context, my code was not able to write the new information to the annotation's parameter. This is perplexing because debug messages were telling me that the correct values were being read from the distribution system and set successfully to the annotation's parameter, but the result did not show this. It showed as if the values from the old distribution system were being written to the annotation's parameter. I attempted to fix this by opening and closing a subtransaction, but that did nothing. Finally, after stumbling across this thread, the problem is fixed by regenerating the document after the distribution system is set.

 

I am still stumped as to why this happened. Why did the operation work fine during a normal transaction, but fail during an updater transaction? Why were the old values written to the parameter even though debug messages showed the new values were being used when setting the parameter? Why didn't a subtransaction regenerate the model, but Document.Regenerate() did?

 

If anyone happens to know the answers, please do tell!

0 Likes