How to closed drawing from non UI thread

How to closed drawing from non UI thread

Scott_FergusonPVMNH
Enthusiast Enthusiast
1,600 Views
8 Replies
Message 1 of 9

How to closed drawing from non UI thread

Scott_FergusonPVMNH
Enthusiast
Enthusiast

I am trying to close a drawing, but when I do I receive an error:

 

System.NotSupportedException: 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'

 

 

        public void CloseDWG()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor editor = doc.Editor;

            if (editor.IsQuiescent)
            {
                doc.CloseAndDiscard();  // <- error here
            }  
        }

 

 

I suppose I need to move the code to the calling thread, I have gotten this far but it doesn't compile.

 

 

Application.Invoke(() =>
            {
                Document doc = Application.DocumentManager.MdiActiveDocument;

                if (doc != null)
                {
                    Editor editor = doc.Editor;

                    if (editor.IsQuiescent)
                    {
                        busyCheckTimer.Stop();

                        // The drawing is no longer busy, take action here
                        doc.CloseAndDiscard();
                    }
                }
            });

 

 

Any help would be appreciated. 🙂 

0 Likes
Accepted solutions (1)
1,601 Views
8 Replies
Replies (8)
Message 2 of 9

norman.yuan
Mentor
Mentor

of course it would not compile: Application.Invoke requires an argument of ResultBuffer. It is mostly used to invoke a List routine.

 

The issue in which execution context you want to close a opened document in AutoCAD. You might as well explain why/how you want to close opened document from other thread other than AutoCAD's UI thread? It sounds to me that your code/app might use AutoCAD in a wrong way (using AutoCAD as unattended server?).

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 9

Scott_FergusonPVMNH
Enthusiast
Enthusiast

The reason I am on another thread is I use a timer to call a method that checks the Editor.IsQuiescent property to see if I can close the drawing or not.

In essence, when a drawing opens, the drawing properties are checked, if certin business rules are not met, a wpf Window is opened to notify the user something is wrong and they should close the drawing.

When the user then clicks a button on this Window (which closes the wpf Window) the closed event is fired. In this event I am attempting to also close the AutoCAD drawing.

Here is more of the code:

 

private void DocumentManager_DocumentBecameCurrent(object sender, DocumentCollectionEventArgs e)
{
     // check dwg properties

      ....

     // something is wrong with the properties
    // show user a form to let user know and close the drawing
     ShowForm();
     ....
}

SomeView_someView;
privare void ShowForm()
{
    _someView = new SomeView();
    _someView.ShowDialog();
}


public class SomeView : Window
{
   public SomeView()
   {
 	InitializeComponent();

        // Subscribe to the Closing event
        Closed += SomeView_Closed;  
   }
	
   private void SomeView_Closed(object sender, EventArgs e)
   {
       if (DataContext is SomeViewModel viewModel)
       {
            viewModel.ClosingCommand.Execute(null);
       }
   } 
   
}

public class SomeViewModel
{ 
    public SomeViewModel()
    {
        ClosingCommand = new RelayCommand(CloseDWG);
    }

    private Timer busyCheckTimer;
    public void CloseDWG()
    {
        Document doc = Application.DocumentManager.MdiActiveDocument;
        Editor editor = doc.Editor;

        if (editor.IsQuiescent)
        {
            doc.CloseAndDiscard();
        }
        else
            StartBusyCheck();

    }

    public void StartBusyCheck()
    {
        busyCheckTimer = new Timer(500); // Check every 1/2 second
        busyCheckTimer.Elapsed += BusyCheckTimerElapsed;
        busyCheckTimer.Start();
    }

    // This is the method that is on another thread
    private void BusyCheckTimerElapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;

            if (doc != null)
            {
                Editor editor = doc.Editor;

                if (editor.IsQuiescent)
                {
                    busyCheckTimer.Stop();

                    // The drawing is no longer busy, take action here
                    doc.CloseAndDiscard();
                }
            }
        }
        catch (Exception ex)
        {
            _logger.Log(ex.Message, ex.StackTrace, LogType.Error);
        }
    }
}
0 Likes
Message 4 of 9

Scott_FergusonPVMNH
Enthusiast
Enthusiast

What is the standard way to wait on the drawing to become quiescence in order to close it? 

0 Likes
Message 5 of 9

Scott_FergusonPVMNH
Enthusiast
Enthusiast
Accepted solution

Ok, I worked it out. Here is what I did.
I put the checker on a background thread. When the Editor.IsQuiesence is true the background thread ends.
This puts me back on the main thread and from here I close the drawing.
Here is my CloseDWGService class that does the work:

public class CloseDWGService : ICloseDWGService
    { 
        public async void CloseDWGAsync()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor editor = doc.Editor;

            if (editor.IsQuiescent)
            {
                doc.CloseAndDiscard();
            }
            else
               await StartBusyCheckAsync();
        }

        
        private async Task StartBusyCheckAsync()
        {
            // Start a background thread
            Task backgroundTask = Task.Run(() => BackgroundWork());
            await backgroundTask;

            CloseDWGAsync();
        }

        static void BackgroundWork()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;

            if (doc != null)
            {
                Editor editor = doc.Editor;
                var loopCount = 0;
                do
                {
                    if (editor.IsQuiescent)
                    {
                        return;
                    }
                    loopCount++;
                    Thread.Sleep(500);
                } while (loopCount<30);
            }
            return;
        } 
    }
0 Likes
Message 6 of 9

norman.yuan
Mentor
Mentor

If your purpose is to test whether a drawing's data meets specific business rules upon its opening, I would not do it in DocumentBecameCurrent event. Even you need to continuously monitor the opened drawing for the rule compliance, I still would not do it in DocumentBecameCurrent event, because this even could happen by other CAD apps/commands and popping up a modal dialog in that even handle would interrupt legit CAD apps/commands and may CAD user frustrated.

 If you only care to test the rule compliance, it would be better to do it in DocumentCreated event, and if violation found, save the message, and add handler for Application_Idle. and show popup message when AutoCAD is idle (then remove the Application_idle handler). I also prefer only prompting user and let user to decide whether to close it or not, as opposed to forcing the close when the popup message is closed.

 

Norman Yuan

Drive CAD With Code

EESignature

Message 7 of 9

Scott_FergusonPVMNH
Enthusiast
Enthusiast

Sounds good, thanks for the advice!  I was noticing the event was firing too often at random times. 

0 Likes
Message 8 of 9

ActivistInvestor
Mentor
Mentor

You can add a Handler for the Application.Idle event to close the active document. When the Handler is invoked it runs in the application context and can close the active document and remove itself from the Idle event so that the Handler doesn't fire again. The Idle event should fire immediately after your window closes and AutoCAD starts polling for input.

 

The code attached to the post at the link below shows some examples of how the Application's Idle event can be used in lieu of asynchronous wait loops to solve these types of problems.

 

https://forums.autodesk.com/t5/net/overkill-on-selection-set/m-p/7683139/highlight/true#M56841https://forums.autodesk.com/t5/net/overkill-on-selection-set/m-p/7683139/highlight/true#M56841 

Message 9 of 9

Scott_FergusonPVMNH
Enthusiast
Enthusiast

Thanks for the links! I really appreciate it. 🙂

0 Likes