在我的窗体类中,我添加了一个方法来“淡出”它。这利用了System.Timers.Timer
,而Elapsed
事件使用委托来更改窗体的不透明度。代码是这样的:
public void FadeOut()
{
// Timer for transition
Timer fade = new Timer();
// Transition code
fade.Elapsed += delegate
{
this.Opacity += 0.05;
if (this.Opacity >= .95)
{
this.Opacity = 1;
fade.Enabled = false;
fade.Dispose();
}
};
fade.Interval = 100;
fade.Enabled = true;
}
这导致了“跨线程操作无效”的错误,这是我看到的一个常见的障碍。因此,我四处寻找解决方案,第一个解决方案涉及到使用.BeginInvoke
和代码块来保持对与控件相同的线程的调用。但我发现它看起来真的很笨重,所以我继续寻找,然后发现了System.Timers.Timer
的SynchronizingObject
属性。这看起来更好,因为它只需要额外的一行代码:
// Timer for transition
Timer fade = new Timer();
fade.SynchronizingObject = this;
代码现在运行得很好。但我真的很困惑,为什么很多解决方案都建议使用BeginInvoke/Invoke
,而所有需要的都是将SynchronizingObject
设置为表单控件?
发布于 2011-01-31 20:01:45
我不确定,但我相信计时器内部也会在SynchronizingObject
属性上使用Invoke
或BeginInvoke
。
让我们假设这个属性只是给了开发人员一些抽象;让他的生活更容易。
我的猜测确实是正确的,这是Reflector告诉我们关于System.Timers.Timer
的MyTimerCallback
私有成员方法的信息
ElapsedEventHandler onIntervalElapsed = this.onIntervalElapsed;
if (onIntervalElapsed != null)
{
if ((this.SynchronizingObject != null) && this.SynchronizingObject.InvokeRequired)
{
this.SynchronizingObject.BeginInvoke(onIntervalElapsed, new object[] { this, e });
}
else
{
onIntervalElapsed(this, e);
}
}
发布于 2011-01-31 20:29:59
主要是因为使用该属性是没有意义的。是的,它确保Elapsed事件处理程序在UI线程上运行。但现在它只是做与System.Windows.Forms.Timer相同的事情。
虽然不完全是这样,但情况更糟。因为它不能保证在禁用它之后不会调用Elapsed。禁用它不会刷新任何挂起的调用,也不会刷新尚未准备好运行的TP线程。如果间隔与处理程序所做的工作量相比很小,则可能会有数百个。
在这里,您绝对需要一个System.Windows.Forms.Timer。您没有在线程池线程上做任何有用的工作。
发布于 2011-01-31 20:04:28
你为什么不用WinForms timer呢?这是基于窗口消息的,并且将始终在UI线程中运行;由于您希望在UI线程需要提取消息的地方执行更新,因此这可能是一个更好的解决方案(不需要同步/阻塞)。
https://stackoverflow.com/questions/4850564
复制相似问题