.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Detecting the Undo of a Custom Command Instance

15 REPLIES 15
Reply
Message 1 of 16
namin
716 Views, 15 Replies

Detecting the Undo of a Custom Command Instance

I have a command which adds some entities to the drawing and changes some custom internal state. If the user undoes the command, I would like to also restore the custom internal state. Is there a way to detect a particular undo and hook into it in order to run a custom action?

Thanks.
15 REPLIES 15
Message 2 of 16
Anonymous
in reply to: namin

The easiest way to do that is to store your internal state
data in the drawing (a Dictionary perhaps), and access it
directly.

Because changes you make to the data stored in the
drawing are undone/redone along with everything else,
your state will always be in sync with the rest of the
drawing.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6032949@discussion.autodesk.com...
I have a command which adds some entities to the drawing and changes some custom internal state. If the user undoes the command, I would like to also restore the custom internal state. Is there a way to detect a particular undo and hook into it in order to run a custom action?

Thanks.
Message 3 of 16
namin
in reply to: namin

Thanks, Tony, for the quick reply.

I like your suggestion. Would you please tell me more about how I can store my internal state in the drawing? Do you have a pointer to an article, perhaps?

I am wondering whether I can store an arbitrary .NET object in the drawing, as I am already making heavy use of my internal state.

Thanks, again.
Message 4 of 16
Anonymous
in reply to: namin

Search the newsgroup for "XRecord" and you should find some examples.

XRecords are the storage medium used to store arbitrary data in a drawing. XRecords must be contained in a Dictionary and a Dictionary can be attached to any object, or be stored in the 'named objects dictionary'.

You can't store managed objects in a drawing directly, but can write an object's data or state to an XRecord manually and read it back into the object later (provided the data types you want to persist are supported
by the XRecord).

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6032971@discussion.autodesk.com...
Thanks, Tony, for the quick reply.

I like your suggestion. Would you please tell me more about how I can store my internal state in the drawing? Do you have a pointer to an article, perhaps?

I am wondering whether I can store an arbitrary .NET object in the drawing, as I am already making heavy use of my internal state.

Thanks, again.
Message 5 of 16
namin
in reply to: namin

Thanks, Tony. I found this example:
http://through-the-interface.typepad.com/through_the_interface/2006/11/linking_circles_1.html

However, Kean allows the data to get out of the sync with the internal state. Now, I am not really sure your suggestion is good, because it means that I have to constantly re-read my managed internal state from the XRecord in order to make sure the undos are taken into account. Do you agree that's quite painful?

Would you perhaps have a suggestion on how to go about with my original idea, which is to monitor the undos?

Thanks. I am a little lost about the best way to handle this.
Message 6 of 16
Anonymous
in reply to: namin

>> Now, I am not really sure your suggestion is good, because it means that I have to constantly re-read my managed internal state from the XRecord in order to make sure the undos are taken into account. <<

Sorry, I don't follow. The whole point of storing the data in
the drawing, is so that it will be undone/redone in order to
eliminate any need to 'make sure the undos are taken into
account', because the data is managed by AutoCAD.

Whenever you access it, it is correctly synchronized with
any other drawing data.

What Kean does in that article is in my opinion, unsound,
for the very reason cited. He stores the data in memory,
and in the drawing. I don't see the point to that, because
you have to read the data from the drawing anyway, since
the copy stored in memory is not necessarily up to date.

I just store the data in the drawing. The overhead of
reading/writing it is not too significant in most realistic
scenarios where the need to access it is infrequent.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm
Message 7 of 16
namin
in reply to: namin

OK, thanks. I appreciate your point, specially since Kean's example actually fails with undo.

I am thinking of just keeping a trace in the drawing, and by looking at the trace, I will know which 'version' of my internal state to use. It seems very tedious for me to have to convert all my internal state into the XRecord format. But you've convinced me that your approach is sound.

Thanks again.
Message 8 of 16
Anonymous
in reply to: namin

Thank you, Tony. Allways interesting to hear your opinion.

Roland
Message 9 of 16
Anonymous
in reply to: namin

Converting your class to store state in the drawing isn't
that difficult.

I use properties whose getter and setter methods read
and write the data to the XRecord directly.

The other advantage to storing state in the drawing, is
that if the state is document-specific (meaning that you
have different instances of your class with different state,
one for each open document), it solves that problem quite
nicely as well.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6032999@discussion.autodesk.com...
OK, thanks. I appreciate your point, specially since Kean's example actually fails with undo.

I am thinking of just keeping a trace in the drawing, and by looking at the trace, I will know which 'version' of my internal state to use. It seems very tedious for me to have to convert all my internal state into the XRecord format. But you've convinced me that your approach is sound.

Thanks again.
Message 10 of 16
namin
in reply to: namin

Thanks a lot, Tony. I like the idea of using getters and setters that read and write the XRecord directly.

I have a question about how to format a variable-length modifiable list into an XRecord, but I'll ask it in a separate thread, as it's somewhat orthogonal to this issue.

Thanks, again!
Message 11 of 16
foruma000256
in reply to: namin

Tony's idea is very sound, in fact my team practices it as well. However if you wish to attempt a tracking of the Undo command to possibly roll back some changes you made, you need to follow some not so simple steps:

Listen to CommandWillStart
filter for e.GlobalCommandName to get the "U" undo command name, then flag it.
IF flagged, then collect object ID's processed by the Undo command
Listen to the EnteringQuiescentState
If your undo flag was set, then process the undo IDs for something that is of your project (a block reference you created for example)
If one of your objects was found, then goto town on it...
Clear your undo flag.

Here is a snippet of my code and all the Events that I listen too when processing an Undo command:

Private Sub callback_CommandWillStart(ByVal sender As Object, ByVal e As CommandEventArgs)
If booIgnoreCommandWillStart Then Exit Sub
'Debug.Print("CWS: " & e.GlobalCommandName)
'Dim ibe As Boolean = Me.IgnoreEvents
Try
Select Case e.GlobalCommandName
Case "U"
IgnoreEvents = True 'listen to no events
booIgnoreObjectModified = False 'ensure object modified is listening
booIgnoreObjectAppended = False
booIgnoreObjectErased = False
booIgnoreObjectReappended = False
booIgnoreObjectUnappended = False
booUndo = True 'turn on the undo flag so we collect IDs instead of process them actively
booIgnoreCommandEnded = False 'turn on the command ended to listen so it can deactivate these events

That's about it, but it takes a lot of code, practice, and patience. Good luck.

jvj
Message 12 of 16
Anonymous
in reply to: namin

What you suggest does not work, because changes
to a drawing can be undone without any commands
starting or ending.

For example, right-click on the Properties palette
and choose "Undo".

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2009
Supporting AutoCAD 2000 through 2009

http://www.acadxtabs.com

Introducing AcadXTabs 2010:
http://www.caddzone.com/acadxtabs/AcadXTabs2010.htm

wrote in message news:6033553@discussion.autodesk.com...
Tony's idea is very sound, in fact my team practices it as well. However if you wish to attempt a tracking of the Undo command to possibly roll back some changes you made, you need to follow some not so simple steps:

Listen to CommandWillStart
filter for e.GlobalCommandName to get the "U" undo command name, then flag it.
IF flagged, then collect object ID's processed by the Undo command
Listen to the EnteringQuiescentState
If your undo flag was set, then process the undo IDs for something that is of your project (a block reference you created for example)
If one of your objects was found, then goto town on it...
Clear your undo flag.

Here is a snippet of my code and all the Events that I listen too when processing an Undo command:

Private Sub callback_CommandWillStart(ByVal sender As Object, ByVal e As CommandEventArgs)
If booIgnoreCommandWillStart Then Exit Sub
'Debug.Print("CWS: " & e.GlobalCommandName)
'Dim ibe As Boolean = Me.IgnoreEvents
Try
Select Case e.GlobalCommandName
Case "U"
IgnoreEvents = True 'listen to no events
booIgnoreObjectModified = False 'ensure object modified is listening
booIgnoreObjectAppended = False
booIgnoreObjectErased = False
booIgnoreObjectReappended = False
booIgnoreObjectUnappended = False
booUndo = True 'turn on the undo flag so we collect IDs instead of process them actively
booIgnoreCommandEnded = False 'turn on the command ended to listen so it can deactivate these events

That's about it, but it takes a lot of code, practice, and patience. Good luck.

jvj
Message 13 of 16
foruma000256
in reply to: namin

Thanks for the heads up Tony, all this time later and I still haven't addressed that issue. However, now, it sounds like another challenge...

I'll get back to you on that one, unless you want to save me the time.
Message 14 of 16
foruma000256
in reply to: namin

I used the EventWatcher to test for this and found that properties palette throws the Document lock mode changed event, then pushes the command UNDO_API while processing. Therefore, when that happens, one only needs to capture the object IDs processed during that time, then clean them up afterwards (after is Quiescent state change).

For review and possible scruitiny, I have posted my DocumentLockModeChanged event handler that was already in place for other stealthy commands of this nature. The point of this type of handler to only to FLAG the need to do something later, because processing during these commands tends to cause serious Conflict troubles.

Public Sub callback_DocumentLockModeChanged(ByVal sender As Object, ByVal e As DocumentLockModeChangedEventArgs)
If booIgnoreDocumentLockModeChanged Then Exit Sub
'Debug.Print("DLMC: " & e.GlobalCommandName)
Select Case e.GlobalCommandName
Case "GRIP_EDIT"
booGripEdit = True
Case "LAYER CONTROL"
booGripEdit = True
Case "AUTO_SAVE"
IgnoreEvents = True
booIgnoreDocumentLockModeChanged = False
Case "QSAVE"
IgnoreEvents = True
booIgnoreDocumentLockModeChanged = False
Case "#AUTO_SAVE"
IgnoreEvents = False
Case "#QSAVE"
IgnoreEvents = False
Case ""
ChPropEvent = True
Case "UNDO_API"
'ChPropEvent = True
IgnoreEvents = True 'listen to no events (then activate only the specific events needed
booIgnoreObjectModified = False 'ensure object modified is listening
booIgnoreObjectAppended = False
booIgnoreObjectErased = False
booIgnoreObjectReappended = False
'booIgnoreObjectUnappended = False
booUndo = True 'turn on the undo flag so we collect IDs instead of process them actively
booIgnoreCommandEnded = False 'turn on the command ended to listen so it can deactivate these events
Case "#UNDO_API"
IgnoreEvents = False
Case Else
End Select
End Sub

Later
Message 15 of 16
foruma000256
in reply to: namin

Still hot on it's trail, I can collect the id's being worked on during the undo event, but I can't seem to find an appropriate 'event' if there is any, to determine when to process these id's.

I can't run during the document lock mode changed event, because apparently (due to the error reports) AutoCAD is STILL running the api's commands. So i watched for the LAST lock mode global command it runs ("#PARTIALREGEN") and it still errors, out still running command.

So I payed attention to my Command Ended event and I get nothing. Not even fired off once.

So now it is either deeper into the rabbit hole or off onto the properties palette class, or not managed at all, and down into windows processing. ALL of which I currently know almost nothing about. Feel free to shine a light on the subject if anybody knows anything.

Thanks,

jvj
Message 16 of 16
foruma000256
in reply to: namin

The trail went cold... For now I'm going to have be satisfied with the fact that I can collect the IDs. I will just wait on the user to make a move (run another normal command) so that I can process them during ed.EnteringQuiescentState.

jvj

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

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost