为相当长的帖子和大量代码提前道歉。
我的应用程序具有定时自动保存功能。用户要求我提供剩余时间的可视指示器。我做了一些关于倒数计时器的研究,并最终写下了这个类:
public class CountDownTimer
{
private Timer timer;
private int remaining;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Count down ticked delegate. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
///
/// <param name="remaining"> The remaining. </param>
/// <param name="maximum"> The maximum. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void CountDownTickedDelegate(int remaining, int maximum);
/// <summary> Event queue for all listeners interested in Ticked event. </summary>
public event CountDownTickedDelegate Ticked;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Count down percent delegate. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
///
/// <param name="percent"> The percent. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void CountDownPercentDelegate(int percent);
/// <summary> Event queue for all listeners interested in Percent events. </summary>
public event CountDownPercentDelegate Percent;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Count down done delegate. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public delegate void CountDownDoneDelegate();
/// <summary> Event queue for all listeners interested in Done events. </summary>
public event CountDownDoneDelegate Done;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Gets or sets the maximum value to count down from </summary>
///
/// <value> The maximum value. </value>
////////////////////////////////////////////////////////////////////////////////////////////////////
public int Maximum
{
get;
set;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Gets or sets a value indicating whether the timer is Paused. </summary>
///
/// <value> true if paused, false if not. </value>
////////////////////////////////////////////////////////////////////////////////////////////////////
public bool Paused
{
get;
set;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Starts this CountDownTimer. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void Start()
{
timer = new Timer {
Interval = 1000
};
timer.Tick += onTimerTick;
timer.Enabled = true;
remaining = Maximum;
Paused = false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Stops this CountDownTimer. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void Stop()
{
if (timer == null)
{
return;
}
Paused = true;
timer.Enabled = false;
timer = null;
if (Percent != null)
{
Percent(100);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Resets and restarts this CountDownTimer. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
////////////////////////////////////////////////////////////////////////////////////////////////////
public void Reset()
{
Stop();
Start();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Handles the timer tick event. </summary>
///
/// <remarks> Jon, 18/06/2012. </remarks>
///
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Event information to send to registered event handlers. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
private void onTimerTick(object sender, EventArgs e)
{
if (remaining > 0)
{
if (Ticked != null)
{
Ticked(remaining, Maximum);
}
if (Percent != null)
{
int percent = remaining * 100 / Maximum;
Percent(percent);
}
if (!Paused)
{
remaining--;
}
else
{
remaining = Maximum;
}
}
else
{
Stop();
if (Done != null)
{
Done();
}
}
}
}
我正在使用一个计时器,每次“触发”我都会减少一个计数器。每个减量开始一个事件,以便我的表格可以在视觉上呈现它。当计数器达到零时,另一个事件将启动自动保存。
如果用户手动保存或打开新项目,还包含一些其他位以允许重新启动自动保存。
它似乎对我有用。但是,用户报告计时器运行的时间越长,自动保存之间的间隔越短。我将计时器设置为每秒打勾,我的调查表明它以两倍的速度运行。因此,如果计数器设置为60(秒),那么它每30次运行一次。我无法复制用户看到的行为,但他的日志肯定显示运行速度太快。
这与主应用程序在同一个主题中 - 可能是一个问题。到目前为止,我所有的测试都没有进行任何改动,除了嘀嗒似乎每隔三次左右连续发射两次。
非常感谢任何见解。
答案 0 :(得分:1)
我看到的一个问题是,如果CountDownTimer.Start()
被调用两次(甚至多次),没有相应的CountDownTimer.Stop()
次调用,则最终会有两个或多个已激活的{{1}实例对象,都调用你的Timer
事件处理程序。
这可能会导致您描述的效果,因为所有正在运行的onTimerTick()
个实例会分别减少剩余的迭代次数。
您的通话代码可以吗?
修改强>
作为一种解决方法,我建议您从Timer
内拨打Stop()
。或者更好的是,您不会为每个新的倒计时操作重新创建Start()
对象。在构造函数中创建Timer
对象,并仅操纵其属性。
在部署Timer
对象时,从onTimerTick()
实例中删除timer
事件处理程序也不是一个坏主意。否则,GC无法收集Timer
实例,因为它仍然保留对其timer
实例的引用。
答案 1 :(得分:0)
计时器的Elapsed事件会在每个间隔后继续触发。您需要先在Elapsed事件处理程序方法中停止计时器,然后在离开方法时再次启动它。否则,如果事件处理程序方法中的代码大约需要1秒,那么Elapsed事件将再次引发。 e.g。
private void onTimerTick(object sender, EventArgs e)
{
try
{
timer.Stop();
//Do your stuff here
}
finally { timer.Start(); }
}