tags:

views:

118

answers:

3

I'm making a Calendar application for myself to use, and to learn.

I've had no trouble until now with mutliple forms, and opening new ones on top of each other, etc.

Here's an example:

private void button1_Click(object sender, EventArgs e)
{
    if (ceForm != null) ceForm.Close();
    ceForm = new CalendarEventForm();
    ceForm.Show();
}

Anyway, I now started to add timers to pop up a 'reminder' form before important events on my calendar will occur (i.e. 1 hour before etc.).

The code sets up the timers when the program is loaded, and then when each timer elapses, this is called:

static void lazyTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    mainForm.ShowReminder((sender as LazyTimer).ReferredEvent);
}

LazyTimer is exactly the same as a System.Timers.Timer except the added propery 'ReferredEvent', which refers to the calendar event that is to be reminded of.

MainForm.ShowReminder() is as follows:

public void ShowReminder(LazyEvent lazyEvent)
{
    ReminderForm newReminder = new ReminderForm();
    newReminder.LoadEvent(lazyEvent);
    newReminder.Show();
}

The weird thing is that ReminderForm crashes. I've tried it with other forms (such as CalendarEventForm, which I know works normally) and they crash too. However, when I try to load the ReminderForm by pressing a button on my main form, it works fine.

Why do my forms crash when loaded (indirectly) by a timer?

+6  A: 

If it's wrapping a System.Timers.Timer, it will be firing on a thread-pool thread, which means you can't do UI operations there.

Use a System.Windows.Forms.Timer instead, or set the SynchronizingObject in the System.Timers.Timer to a UI object, so that the timer will fire on the UI thread.

EDIT: Another point... personally I'd probably use a lambda expression or anonymous method as the timer's Tick event handler, capturing the relevant event that way and thus avoiding the extra class and the extra method:

// Presumably we've got a local variable here, e.g. currentEvent
timer.Tick += delegate { mainForm.ShowReminder(currentEvent; };
Jon Skeet
+8  A: 

Short answer: Use a System.Windows.Forms.Timer, not a System.Timers.Timer.

The reason is that the System.Timer.Timers class will fire the timer event on another thread, and you are not able to directly do UI operations from another thread than the main UI thread.

0xA3
A: 

You are running into a threading issue.

Please use System.Windows.Forms.Timer when working with System.Windows.Forms.

The System.Timers.Timer does not invoke the event on the applications event loop, but calls the event handler directly, which leads, in your case, to a cross thread operation which is not supported by Forms, and your application crashes.

In contrast, System.Windows.Forms.Timer will fit seamlessly into the component model of System.Windows.Forms.

Emiswelt