I would like to be able to have the screen refresh periodically to show the new objects I have added as a command method proceeds to execute. I am hoping to be able to achieve a kind of simple animation by doing this, and also to show progress while a computationally intensive operation is under way. Is there a way that I can do this?
I have tried using AcadApp.DocumentManager.MdiActiveDocument..Editor.Regen() and also .Editor.UpdateScreen(), but it only seems to work once and not multiple times. Do I have to commit the transaction and then .Regen()?
Thanks!
Solved! Go to Solution.
Solved by DiningPhilosopher. Go to Solution.
I tried calling TransactionManager.Database.QueueForGraphicsFlush(), and also tried calling Editor.Regen() immediately after it, but when I run my command the screen only ever redraws after the entire command is complete. So, the visual effect for a command that takes several seconds and is drawing a lot to the screen is that there is a pause of several seconds, followed by everything showing up at once.
Is there perhaps some other command to tell the system to process events? Transients seem to do this. But I don't really want to draw transients. I just want actual entities to appear as a command progresses.
Thanks!
What I even notice with the Transient object is that if the command is busy doing something, even calling TransientManager.CurrentTransientManager.UpdateTransient() does not update the screen until after the command function has exited. There seems to be no way to force the current view to update while a command is executing. What this seems to imply is that if I want to truly show the progress of something being drawn I need spin up a transient, allow the command to exit, and then draw everything using the Transient object. That seems like a lot of extra work, because really I just want to commit real objects to the database and refresh the screen as I go.
Any idea are appreciated!
Perhaps I misunderstand what you want to do but this is working for me:
[CommandMethod("Test")] public void Test() { Document doc = AcAp.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; PromptResult pr = ed.GetString("\nEnter a string: "); if (pr.Status != PromptStatus.OK) return; string str = pr.StringResult; Matrix3d ucs = ed.CurrentUserCoordinateSystem; PromptPointOptions options = new PromptPointOptions("\nInsertion point: "); options.AllowNone = true; PromptPointResult ppr; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord space = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite); while (true) { ppr = ed.GetPoint(options); if (ppr.Status != PromptStatus.OK) break; DBText text = new DBText(); text.TextString = str; text.Position = ppr.Value.TransformBy(ucs); space.AppendEntity(text); tr.AddNewlyCreatedDBObject(text, true); db.TransactionManager.QueueForGraphicsFlush(); } tr.Commit(); } }
Yes I understand. I believe that works for you because in each pass through your loop you pause for user input. Pausing for user input returns control to Autocad so the user can specify the next point. At that moment it is possible for Autocad to refresh the screen. But imagine if you were not pausing for user input, but were instead computing the next point via some mathematical procedure. What you would see is that all your lines appear at once. This doesn't happen simply because the lines are being drawn really fast; it happens because Autocad does not have the opportunity to redraw the screen. So, let's say you were drawing 100 lines in your command and the procedure was complex enough that it took 1/10 of a second to figure out the end points of each line. You would observe that Autocad freezes for about 10 seconds and then all 100 lines would appear at once instead of sequentially.
The only method I've seen for truly drawing things dynamically is using Transients as shown here. Perhaps that's the only way to do it. In this method control does return to Autocad (the command method exits), but the Transient continues to run in the background by means of Windows Messages being fired.
Hi Gile.
The reason your code works is because it acquires user input in the loop, which causes AutoCAD to update the display.
In fact, there is an undocumented method in Autodesk.AutoCAD.Internal.Utils that seems to be precisely for that purpose, called FlushGraphics(), and all it does is call acutPrompt().
You can post some code snippets showing more precisely what it is you've tried that isn't working.
You need to use the TransactionManager of the Document object, not the TransactionManager of the Database, and you need to commit the transaction in order for AutoCAD to update changes to the display, in addition to calling EnableGraphicsFlush() and QueueForGraphicsFlush()
If you're using Transient graphics, you can try one of Kean's hacks that is supposed to force AutoCAD to process any pending window messages (I haven't tried this so I can't vouch for it):
// Using System.Windows.Forms; // Using System.Drawing; Point p = Cursor.Position; Cursor.Position = new Point(p.X, p.Y);
Another thing yhou can try is calling the undocumented Autodesk.AutoCAD.Internal.Utils.FlushGraphics() method.
If you're modifying objects in the database, you will need to commit the transaction in which the changes were made to those objects You can use nested transactions to periodically update graphics without making the individual changes in each nested transaction undoable.
Thank you! That worked. The final answer was, I had to do everything suggested above. I committed the transaction periodically, and upon the commit my C# code looks like
_document.TransactionManager.EnableGraphicsFlush(true);
_document.TransactionManager.QueueForGraphicsFlush();
Autodesk.AutoCAD.Internal.Utils.FlushGraphics();
_transaction.Commit();
_transaction.Dispose();
where _document and _transaction are just the usual Autocad objects I assigned to member variables of a utility class.
In case you end up here like I did from a Google search, and are working on an ObjectARX app, none of the above worked for me.
This did:
Acad::ErrorStatus es;
es = actrTransactionManager->enableGraphicsFlush(true) ;
es = actrTransactionManager->queueForGraphicsFlush() ;
AcTransaction *pTrans = actrTransactionManager->startTransaction();
.. do stuff here..
actrTransactionManager->flushGraphics() ;
actrTransactionManager->endTransaction();
acedUpdateDisplay();
Not really sure if order of operations matters.
@Anonymous wrote:In case you end up here like I did from a Google search, and are working on an ObjectARX app, none of the above worked for me.
This did:
Acad::ErrorStatus es;
es = actrTransactionManager->enableGraphicsFlush(true) ;
es = actrTransactionManager->queueForGraphicsFlush() ;
AcTransaction *pTrans = actrTransactionManager->startTransaction();
.. do stuff here..
actrTransactionManager->flushGraphics() ;
actrTransactionManager->endTransaction();
acedUpdateDisplay();
Not really sure if order of operations matters.
Autodesk.AutoCAD.Internal.Utils.FlushGraphics() isn't available in native ObjectARX, but the underlying function it calls (acutPrompt) is, so can we assume you called that in lieu of the former?
Maybe I wasn't doing something very complicated, but this worked for me:
Application.UpdateScreen()
Application.DocumentManager.MdiActiveDocument.Editor.UpdateScreen()
Can't find what you're looking for? Ask the community or share your knowledge.