0

I've problem with ending timer operation in user control. I created User Control with some operation, occurs in intervals using Timer. I want to achieve this goal: when user close windows with my User Control, operation (in Calculate() method) is stopped. This is my code in MyUserControl.cs:

   // fields
   private Timer timer; 

    // ctor
    public TestUserControl()
    {
       InitializeComponent();
       timer = new Timer();   
       timer.Tick += timer_Tick;
       timer.Interval = 5000;
       timer.Start();
    }

    void timer_Tick(object sender, EventArgs e)
    {
        Compute();
    }

    void ParentForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        timer.Stop();
    }

    private static void Compute()
    {
       // do something
    }

And this line in TestUserControl.Control.Desinger.cs in InitializeComponent() method:

this.ParentForm.FormClosing += new System.Windows.Forms.FormClosingEventHandler(ParentForm_FormClosing);

But I get exception in this line: An unhandled exception of type 'System.NullReferenceException'

grigorij89
  • 33
  • 6
  • Why do you need to do this at all?...when the Form containing your UserControl is closed, your Timer will be stopped, and the UserControl disposed of automatically along with all the other controls in the Form. Now, if Compute() happens to be running when the Form is closed, then it would finish before the Form starts its closing process (unless you've done something funky with DoEvents). If you need Compute() to stop prematurely, then you'll have to modify that code to periodically check for some kind of "closing" flag so it can exit and allow the Form to continue closing normally. – Idle_Mind Nov 23 '14 at 18:11
  • I'm doing some database operation, update datagrid in some intervals. I want to end this operation on Form closing. – grigorij89 Nov 23 '14 at 18:16
  • @Idle_Mind: "when the Form containing your UserControl is closed, your Timer will be stopped" -- this is not true for a `Timer` instance that is, as is the case here, explicitly declared and initialized instead of being added to the control via the Designer. – Peter Duniho Nov 23 '14 at 18:50
  • @PeterDuniho, You're right; good catch. – Idle_Mind Nov 24 '14 at 03:21

1 Answers1

0

The main problem with your current attempt is that when the InitializeComponent() method is called, from the constructor, the control does not yet have a Parent value. That's the null value the exception is telling you about.

See What is a NullReferenceException and how do I fix it?

The most straightforward way to address this would be first to remove all of the Timer initialization code, and the Timer field as well, and then to use the Designer to add the Timer to your UserControl. Then you don't even have to subscribe to the parent's FormClosing event in the first place.

You can find the Timer object in the Designer's Toolbox window, under the category of "Components". You can drag or double-click it to add it to your UserControl just like any other Toolbox item. Instead of being placed on the UserControl itself, it will be placed in an area at the bottom of the Designer's own editing window for your UserControl. You can select it there and set properties like Enabled and Interval, as well as add an event handler for the Tick event.

If you configure it to have the Enabled property set to true, then it will start as soon as your UserControl is created. Regardless, by adding to the UserControl in this way, it winds up in the control's own list of components and will be automatically disposed (and thus stopped) when the UserControl itself is disposed, i.e. when the parent form is closed.

Do note that as the comment from "Idle_Mind" points out, because your Tick handler is executed on the UI thread, nothing else will happen on the UI thread while it's running. This means (among other things) that if the Compute() method takes some time to complete, the user could perceive a delay from when they try to close the window and when it actually is closed.

Since any event (e.g. mouse click, Alt+F4, etc.) that would normally signal the window to close can't even be processed during this time, there's really no good way to change this behavior (there's a bad way, which I refuse to describe here). You won't even receive that signal from the user until the Compute() method has completed.

If you need to be able to interrupt the Compute() method via user input while it's running, then one option would be to use some timer other than System.Windows.Forms.Timer, so that the timer handler will run on a separate thread and leave the UI thread responsive to user input. Another option would be to change the Compute() method to an async method so that even though the timer event itself is handled on the UI thread, the database operation is handled asynchronously without blocking the UI thread. Either option would be a whole other question, and for a good answer there would require specific details about your Compute() method showing how you're accessing the database and using the results. :)

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136