views:

228

answers:

2

I have a stopwatch running in a different thread, that updates the GUI thread in a label to show as time goes by. When my program closes, it throws a ObjectDisposedException when I call this.Invoke(mydelegate); in the Form GUI to update the label with the time from the stopwatch.

How do I get rid of this ObjectDisposedException?

I have tried to stop the stopwatch in the FormClosing Event, but it does not handle it.

Here's the code:

System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            stopwatch = sw;

            sw.Start();
            //System.Threading.Thread.Sleep(100);
            System.Threading.Thread t = new System.Threading.Thread(delegate()
            {
                while (true)
                {
                TimeSpan ts = sw.Elapsed;

                string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                ts.Hours, ts.Minutes, ts.Seconds,
                ts.Milliseconds / 10);

                timeElapse = elapsedTime;

                  UpdateLabel();
                }
            });
            stopwatchThread = t;
            t.Start();

 public void UpdateLabel()
        {
            db = new doupdate(DoUpdateLabel);

            this.Invoke(db);
        }

 public void DoUpdateLabel()
        {
            toolStripStatusLabel1.Text = timeElapse;
        }
+2  A: 

What it looks like is the Stopwatch is being disposed when you close your application, but the thread is still running and trying to use it. Can you stop your thread first before closing the application (in the FormClosing event)?

SwDevMan81
I would also highly recommend using a BackgroundWorker for this. I'm glad this solved the problem
SwDevMan81
+1  A: 

Same code, now using a BackgroundWorker and an orderly shutdown that ensures the form doesn't close until the background thread has stopped running first:

using System;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;

namespace WindowsFormsApplication1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
            backgroundWorker1.DoWork += backgroundWorker1_DoWork;
            backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
            backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
            backgroundWorker1.WorkerReportsProgress = backgroundWorker1.WorkerSupportsCancellation = true;
            backgroundWorker1.RunWorkerAsync();
        }
        bool mCancel;
        void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) {
            if (e.Error != null) MessageBox.Show(e.Error.ToString());
            if (mCancel) this.Close();
        }
        protected override void OnFormClosing(FormClosingEventArgs e) {
            if (backgroundWorker1.IsBusy) mCancel = e.Cancel = true;
            backgroundWorker1.CancelAsync();
        }
        void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) {
            label1.Text = e.UserState as string;
        }
        void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
            Stopwatch sw = Stopwatch.StartNew();
            while (!backgroundWorker1.CancellationPending) {
                TimeSpan ts = sw.Elapsed;
                string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",
                    ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
                backgroundWorker1.ReportProgress(0, elapsedTime);
                Thread.Sleep(15);
            }
        }
    }
}

Note that the Sleep() call is required, it isn't possible to marshal calls to the UI thread more than about a 1000 times per second.

Hans Passant
+1 Very nice!!! Thanks for that!!! :)
Tony