0

I have a problem replicated all over the application, but I think it could be managed the same way.

I have an IDisposable class in my Winforms application (it is a custom control derived from System.Windows.Forms.Label)

In that class, I have a timer whose tick event is this:

    private void TickTimer(object state)
    {
        Action updateText = () =>
        {
            this.Parent.SuspendLayout();
            this.Text = DateTime.Now.ToString("HH:mm:ss");
            this.Parent.ResumeLayout(false);
        };

        if (this.InvokeRequired)
            this.Invoke(updateText);
        else
            updateText();
    }

On the other hand, this is the Dispose method:

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Stop();
            _timer.Dispose();
        }
        base.Dispose(disposing);
    }

The problem is when closing the application, an exception is thrown in this.Invoke(updateText) call, telling that the main form (where the control is placed) has been disposed.

Since this occurs assynchronously, How can I manage this?

I have thought about placing a class field called, for example, _isDisposed and in every line of TickTimer, to check if it was disposed, but this is really ugly.

By searching here in StackOverflow, I have found some advices.... none worked. Final attempt was using an "updating" flag.

    private void TickTimer(object state)
    {
        _updating = true;

        Action updateText = () =>
        {
            this.Parent.SuspendLayout();
            this.Text = DateTime.Now.ToString("HH:mm:ss");
            this.Parent.ResumeLayout(false);

            _updating = false;
        };

        if (this.InvokeRequired)
            this.Invoke(updateText);
        else
            updateText();
    }

and in Dispose method:

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            Stop();
            _timer.Dispose();

            while (_updating) ;
        }
        base.Dispose(disposing);
    }

But the same error occurs. It seems that the parent form is disposed before the child controls.

What is the best way to do it?

Jaime

jstuardo
  • 3,901
  • 14
  • 61
  • 136
  • A typical solution: Add a bool member: `private bool _hasBeenDisposed = false;` and then, at the start of your `Dispose(bool)` function, add `if (_hasBeenDisposed) { return; }`. – Flydog57 Jan 18 '19 at 16:31
  • @Flydog57 and when that variable will become true? – jstuardo Jan 18 '19 at 16:35
  • If you want to update the UI from another thread use `async/await` instead of `Invoke`. Even with Invoke you can call a *method*. You don't need an action that may have already been garbage-collected – Panagiotis Kanavos Jan 18 '19 at 16:36
  • Which timer are you using? `System.Windows.Forms.Timer` runs on the UI thread, it doesn't need `Invoke()`. Using `System.Threading.Timer` *with* Invoke defeats its purpose. `Invoke` will try to perform a *blocking* call on the UI thread. Might as well have used the Winforms timer – Panagiotis Kanavos Jan 18 '19 at 16:40
  • @PanagiotisKanavos in this particular case, it is a System.Threading.Timer. This is a control placed in a usercontrol, maybe it was better to use a System.Windows.Forms.Timer? – jstuardo Jan 18 '19 at 16:48
  • @jstuardo: oops, sorry, set the flag at the end of the dispose work, – Flydog57 Jan 18 '19 at 17:11
  • https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose – Johnny Jan 18 '19 at 17:20

0 Answers0