3

Is there any chance that timer_Tick could be called after myForm_FormClosing in the code below.

If there is a chance: Is it sufficient to call timer.Stop() within myForm_FormClosing in order to avoid that timer_Tick gets called after myForm_FormClosing?

using System;
using System.Windows.Forms;
using System.ComponentModel;

namespace Test
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyForm());
        }
    }

    class MyForm : Form
    {
        private IContainer components;
        private Timer timer;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        public MyForm()
        {
            components = new Container();
            timer = new Timer(components);

            timer.Interval = 50;
            timer.Tick += timer_Tick;
            timer.Enabled = true;

            FormClosing += myForm_FormClosing;
        }

        private void timer_Tick(object sender, EventArgs e)
        {
        }

        private void myForm_FormClosing(object sender, FormClosingEventArgs e)
        {
        }
    }
}

Update: After receving a few hints (thanks for helping) I basically have chosen the following code to achive what I want. Please not that timer1_Tick could still be called after myForm_FormClosing was called! This solution just introduces a flag (i called it doWork) which stops the code within timer1_Tick to be executed after myForm_FormClosing was called.

using System;
using System.Windows.Forms;
using System.ComponentModel;

namespace Test
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyForm());
        }
    }

    class MyForm : Form
    {
        private IContainer components;
        private Timer timer;
        private bool  doWork = true;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        public MyForm()
        {
            components = new Container();
            timer = new Timer(components);

            timer.Interval = 50;
            timer.Tick += timer_Tick;
            timer.Enabled = true;

            FormClosing += myForm_FormClosing;
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            if (doWork)
            {
                //do the work
            }
        }

        private void myForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            doWork = false;
        }
    }
}
thiel
  • 39
  • 1
  • 5
  • 1
    Yes and yes. If myForm_FormClosing will be an intense process, just make sure to stop the timer early on. Additionally, if you cancel the close, make sure you restart your timer if necessary. – Jaime Torres Aug 14 '12 at 17:25
  • 1
    @PowerRoy Yes I have tried it out. There was no call of timer_Tick after myForm_FormClosing. But this does not gurantee thre would not be a chance for that. – thiel Aug 14 '12 at 18:18
  • @J-Torres thanks for the answer. But can you explain me why the answer is Yes and yes. Is there any documentation for that. (I did not find anything for that special case.) – thiel Aug 14 '12 at 18:18
  • OP, @JPAlioto Provided a wonderful example of how this can happen. – Jaime Torres Aug 14 '12 at 18:22

3 Answers3

2

Yes it is possible. According to the docs ...

Occurs before the form is closed.

When a form is closed, it is disposed, releasing all resources associated with the form.

The timer will not be disposed until after the FormClosing event. Here is a very contrived example of how it can happen. You will see that you hit the debugger breakpoint on the Timer tick after FormClosing has been called.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private System.Windows.Forms.Timer _time = new System.Windows.Forms.Timer();
        private Task _t1 = null;
        private Task _t2 = null;
        private bool _closingFlag = false;
        public Form1()
        {
            InitializeComponent();

            _time.Interval = 50;
            _time.Tick += (s, e) => { 
                textBox1.Text = DateTime.Now.ToString();
                if (_closingFlag) Debugger.Break();
            };

            _time.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _closingFlag = true;

            _t1 = Task.Factory.StartNew(() => { Thread.Sleep(1000); });
            _t2 = Task.Factory.StartNew(() => { Thread.Sleep(1000); });

            Task.WaitAll(_t1, _t2);
        }
    }
}
JP Alioto
  • 44,864
  • 6
  • 88
  • 112
  • Mmmh... It seems that if i call _time.Stop() at the end in Form1_FormClosing that the Tick-Method is not risen. Can anybody confirm that? – thiel Aug 14 '12 at 19:04
  • @thiel It's still a race condition no matter what you do. If you have come concurrency that you have to let finish, you need to handle that with normal multi-threading approaches. – JP Alioto Aug 14 '12 at 19:18
  • Ok. I think I just set a flag within Form1_FormClosing which is checked within the Tick-EventHandler in order to avoid executing the things which I don't want to be executed. What do you guys think about that? – thiel Aug 14 '12 at 19:25
  • I don't think there is way for that to happen with a `System.Windows.Forms.Timer`. – Luis Quijada Aug 14 '12 at 19:37
  • @LuisQuijada Sorry I don't understand what you mean. – thiel Aug 14 '12 at 19:38
  • Is just what I put at the beginning of my (corrected) answer, the UI thread must be idle for executing the timer tick, and that's not the case when the form is executing the `FormClosing` event handler. – Luis Quijada Aug 14 '12 at 19:42
  • @LuisQuijada I think what you are trying to tell me is that the Tick Event can not be risen within Form1_FormClosing. I know that. My problem is that I have to Close something in the FormClosing-EventHandler. After I have closed that something the code within the Tick-EventHandler **must not** be executed. – thiel Aug 14 '12 at 19:53
  • @thiel Exactly. OK ;). Maybe you need to directly intercept the `WM_CLOSE` message and take the chance at that point to set the control flag. – Luis Quijada Aug 14 '12 at 19:57
  • @LuisQuijada I didn't think about that. But fortunately that something stores a boolean value which indicates if it was closed or not. So I just retrieve that boolean value within the Tick-EventHandler in order decide If I should execute the code. – thiel Aug 14 '12 at 20:09
1

If you use a the timer System.Windows.Forms.Timer the UI thread must be idle to process a tick from the timer, so from my understanding that's is not possible.

Could happen if you use a timer with its own thread, as a System.Timers.Timer. In this case we could avoid what you mention by implementing something like this:

class MyForm : Form
{
    private System.Timers.Timer timer;

    private Object justForLocking = new Object();
    private Boolean safeToProceed = true;

    [...]

    public MyForm()
    {
        components = new Container();
        timer = new System.Timers.Timer();

        timer.Interval = 50;
        timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
        timer.Start();

        FormClosing += myForm_FormClosing;
    }

    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
         // In case that this code is being executed after
         // the method myForm_FormClosing has adquired the 
         // exclusive lock over our dummy resource,
         // the timer thread will wait until the resource if released.
         // Once is released, our control flag will be set to false
         // and the timer should just return and never execute again.
         lock(justForLocking)
         {
             if (safeToProceed)
             {
                 // Do whatever you want to do at this point
             }
         }
    }

    private void myForm_FormClosing(object sender, FormClosingEventArgs e)
    {
         lock(justForLocking)
         {
             safeToProceed = false;
         }
         timer.Stop();

         // Do something else
    }

    [...]
}

This other SO question has relevant information.

EDIT: the code above is only valid if a System.Timers.Timer is used instead of System.Windows.Forms.Timer

Community
  • 1
  • 1
Luis Quijada
  • 2,345
  • 1
  • 26
  • 31
0

I dont believe so but it would be best anyways to call timer.stop() regardless. if you were needing it to though you should create the timer object in program.cs

David
  • 388
  • 1
  • 5
  • 24