C# - 计时器可以调用菜单事件吗?

时间:2009-06-29 18:19:02

标签: c# winforms

我有一个C#Windows窗体应用程序,其中包含一个包含此事件的菜单:

private void createMenuItem_Click(object sender, EventArgs e)
    {
        canvas.Layer.RemoveAllChildren();
        canvas.Controls.Clear();
        createDock();
    }

我想通过另一个启动计时器的菜单选项为用户提供触发此事件的机会。

我的计时器如下所示:

private void transfer_timer()
 {
  System.Timers.Timer Clock = new System.Timers.Timer();
  Clock.Elapsed += new ElapsedEventHandler(createMenuItem_Click); 
  Clock.Interval = timer_interval;
  Clock.Start();
 }

执行此操作时,生成的错误消息为:

createDock - 跨线程操作无效:控制''从其创建的线程以外的线程访问。

我做错了什么?

5 个答案:

答案 0 :(得分:3)

UI元素具有线程关联性。通常,只能从创建的线程调用它们的方法。

您可以在控件上使用Invoke方法进行跨线程调用。 Invoke将封送调用并在UI线程上执行它。

答案 1 :(得分:3)

不要那样做。因为它更像这样......

private void createMenuItem_Click(object sender, EventArgs e)
{
  DoCanvasWork(); // pick a good name :)        
}

private void transfer_timer() {
  System.Timers.Timer Clock = new System.Timers.Timer();
  Clock.Elapsed += new ElapsedEventHandler(Clock_Tick);
  Clock.Interval = timer_interval;  Clock.Start(); 
}

private void Clock_Tick( object sender, EventArgs e )
{
  BeginInvoke( new MethodInvoker(DoCanvasWork) );
}

private void DoCanvasWork()
{
  canvas.Layer.RemoveAllChildren();
  canvas.Controls.Clear();
  createDock();
}

这将为您提供更清晰,更易于维护的代码。您的计时器没有选择菜单,它的工作与选择菜单的工作相同。

评论:欢迎你。乐意效劳!这是关于跨线程封送的good primer(简短阅读,很好的例子)。但是,基本上BeginInvoke正在接受一个委托(MethodInvoker)并在创建控件的线程上异步执行它。在这种情况下,它是跨线程的非阻塞,消息和忘记消息(即我不等待返回,我不会调用EndInvoke来获得返回)。如果您真的想深入了解这一点,Chris Sells book on Winforms是一个很好的资源。

答案 2 :(得分:2)

那是因为System.Timers.Timer使用不同的线程,你不能从不同的线程操作GUI。

最简单的解决方案是使用System.Windows.Forms.Timer。

答案 3 :(得分:1)

由于您收到的错误消息正在尝试解释您创建的Timer对象使用另一个线程来执行回调。 Windows.Forms(或Win32就此问题)无法解决这个问题,这将在here中进行更详细的解释。建议的一个选项是使用`System.Windows.Forms.Timer。或者使用Control.InvokeRequired,Control.Invoke模式,如提供的链接中所述。

答案 4 :(得分:0)

您需要使用System.Windows.Forms.Timer。它总是在UI线程上触发它的事件,而System.Timers.Timer则不会,导致异常。