views:

278

answers:

1

In my C# Windows Forms application, I have a user control that contains other controls and does its own logic. One of them is a delayed call (Timer-invoked) that does certain things after the user has finished a keyboard input (live filter text). It accesses the other controls for this, one of them is that text input control. This method is invoked 500 ms after the last input event.

Now I have a problem when the delayed call is running while the application is terminating. When I enter some text, then wait about 500 ms (it seems to work every time) and then press Alt+F4 to close the window, the application throws a NullReferenceException while trying to access the text input control. This doesn't happen when I close the window immediately after the last input or a second or more after.

It seems that the control is being disposed or something and its methods cannot access the child controls anymore. So, when the control is being put in that state (by whomever and whatever that state exaclty is), those timer need to be stopped first so that the controls can be safely disposed.

I have already tried to stop the timer in the OnHandleDestroyed method (overridden) and at the beginning of the Designer-generated Dispose method. Nothing helped.

This procedure works fine in regular Forms when stopping the timers in the overridden OnFormClosed method, before calling base.OnFormClosed(). I just cannot find a suitable event in a user control.

+1  A: 

Try this in your UserControl:

bool isDisposed;
protected override void Dispose(bool disposeManaged)
{
    if(!isDisposed)
    {
        if(disposeManaged)
        {
            //Dispose your timer here
        }
        isDisposed = true;
    }
}

Another possibility is that one of your UI classes doesn't do its cleanup. Eg. it registers itself for an event but doesn't deregister when it's manually disposed. It is never collected by the GC and when the event is fired for the next time, it tries to access some members that were set to null during the Dispose(...) call before.

Another possibility is that you have a more complex race condition within your code but it's hard to say from here.

racha
This is what I meant with the Dispose method. I added my Timer cancel code right at the beginning of that method so that it would definitely be executed. But it didn't help.
LonelyPixel
Are you sure the _parent_ of your `UserControl` gets its `Dispose` called? The chain has to start somewhere, and usually it's the form, but it never hurts to check.
Pavel Minaev
True, it gets invoked. First the parent control (the Form), then the control itself. But my timer seems to be in the queue already so cancelling the timer doesn't have any effect here. Another solution I've found now is to use the <code>IsDisposed</code> property directly in the method that the timer calls. When <code>Dispose</code> was called (and this all happens synchronised in the UI thread), IsDisposed always returns true, and then I just won't do anything with the controls.
LonelyPixel
Umm, how does formatting work here? There's no documentation right here.
LonelyPixel
So your problem is solved? Do you want to add your own answer?
racha
Yes, my problem is solved. This reply wasn't the solution to my problem, but as I've found out, this question wasn't even my problem... And reply fits question... so: accepted.
LonelyPixel