5

I'm using this code to run ProgressBar in separate thread.

ProgresBarForm viewer = new ProgressBarForm("Wait");    
Thread viewerThread = new Thread(delegate()
    {
        viewer = new ProgressBarForm("Wait");
        viewer.Show();
        System.Windows.Threading.Dispatcher.Run();
    });

viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception
viewerThread.Start();

Then i'm doing some long operation and when i finish i'm invoking this code:

viewer.BeginInvoke(new Action(() => window.Close()));

and it works really well but when I close my window debugger is not stopping. In VS 2012 I click "Break All" button and it occurs that program is hanging on line

System.Windows.Threading.Dispatcher.Run();

How can I close this dispatcher to exit from my program?

Thanks in advance for all responses.

DinosaurTom
  • 212
  • 4
  • 15
  • 1
    This is not how you should be doing this. Your form should be running on a GUI thread. It's the long background processing operation (whatever that is) that should be running on a different thread, and invoke progress bar updates on the GUI thread. The simple way is to use a `BackgroundWorker`. – vgru Nov 30 '14 at 09:06
  • Unfortunately I can not use BackgroundWorker. The problem is that this long operation has to be done alone, because next steps in program depends on this long operation. – DinosaurTom Nov 30 '14 at 09:24
  • 1
    Of course next steps depend on the operation being finished. I think that's how any background running operation works. Stuff depends on it being done. But I don't see what this has to do [with using a `BackgroundWorker`](http://www.codeproject.com/Articles/99143/BackgroundWorker-Class-Sample-for-Beginners). – vgru Nov 30 '14 at 09:46

2 Answers2

8
   viewer.BeginInvoke(new Action(() => window.Close()));

That isn't good enough to get the dispatcher to stop. Your "viewer" is a Winforms Form, not a WPF window. So the normal shutdown mechanism can't work. You have to help and also tell the dispatcher to quit:

   viewer.BeginInvoke(new Action(() => {
       System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown();
       viewer.Close();
   }));

Do beware the race, the user might close your WPF window before you get a chance to call this code. Visions of DoEvents() misery abound. So you probably should also set the thread's IsBackground property to true. And do beware the problems with displaying UI on a worker thread, you have to carefully craft your "progress" form so it doesn't use any of the dangerous controls in the toolbox. The ones that use the SystemEvents class. Or you get to debug a deadlock like this.

This forever works better and more reliably if you do not let your UI thread do the heavy work. Let it take care of just the UI, do the heavy lifting in a worker thread.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
5

Because your thread is a foreground thread, which is enough to keep your process alive.

Make it a background thread by setting viewerThread.IsBackGround = true, so that it will not be able to keep your process alive.

Or inside the callback where you close the window, simply call Dispatcher.InvokeShutdown after closing the window.

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • 1
    The latter suggestion is actually better. Making the thread background will only allow CLR to abort it, but OP's problem is that the thread is stuck inside the call to `Dispatcher.Run`. – vgru Nov 30 '14 at 09:48
  • 1
    viewerThread.IsBackground = true actually helps but it is safe to use? InvokeShutdown doesn't work. (i'm caliing this function in FormClosing event like System.Windows.Threading.Dispatcher.CurrentDispatcher.InvokeShutdown(); – DinosaurTom Nov 30 '14 at 10:02