奇怪的Winforms Bug?

Xen*_*ate 1 c# winforms

我正在制作一个日历应用程序供我自己使用和学习.

到目前为止,我已经毫无困难地使用多种形式,并在彼此之上打开新的等等.

这是一个例子:

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

无论如何,我现在开始添加计时器,以便在我日历上的重要事件发生之前弹出一个"提醒"表单(即1小时前等).

代码在加载程序时设置定时器,然后当每个定时器过去时,这称为:

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

LazyTimer与System.Timers.Timer完全相同,除了添加的属性"ReferredEvent",它引用了要提醒的日历事件.

MainForm.ShowReminder()如下:

public void ShowReminder(LazyEvent lazyEvent)
{
    ReminderForm newReminder = new ReminderForm();
    newReminder.LoadEvent(lazyEvent);
    newReminder.Show();
}
Run Code Online (Sandbox Code Playgroud)

奇怪的是ReminderForm崩溃了.我已经尝试过其他形式(例如CalendarEventForm,我知道它可以正常工作)并且它们也崩溃了.但是,当我尝试通过按下主窗体上的按钮加载ReminderForm时,它工作正常.

为什么我的表单在被计时器加载(间接)时会崩溃?

Dir*_*mar 9

简短回答:使用a System.Windows.Forms.Timer,而不是a System.Timers.Timer.

原因是System.Timer.Timers该类将在另一个线程上触发timer事件,并且您无法直接从主UI线程之外的另一个线程执行UI操作.


Jon*_*eet 6

如果它正在包装a System.Timers.Timer,它将在线程池线程上触发,这意味着你不能在那里进行UI操作.

使用System.Windows.Forms.Timer替代,或者设置SynchronizingObject的System.Timers.Timer一个UI对象,从而使计时器会触发UI线程.

编辑:另一点......我个人可能会使用lambda表达式或匿名方法作为计时器的Tick事件处理程序,以这种方式捕获相关事件,从而避免额外的类额外的方法:

// Presumably we've got a local variable here, e.g. currentEvent
timer.Tick += delegate { mainForm.ShowReminder(currentEvent; };
Run Code Online (Sandbox Code Playgroud)