Я бы, вероятно, нарушил функционал управления таймером здесь; что-то вроде этого (примечание: я пишу это на лету, поэтому дайте мне знать, если это не работает как есть, и я помогу исправить любые проблемы):
// a simple delegate to report the amount of time remaining
// prior to the expiration of the major tick interval; zero
// indicates that this major tick has elapsed.
public delegate void delegateMajorMinorTimerTick
(
int TimeRemaining_sec, ref bool CancelTimer
);
// you could use milliseconds for the interval settings to get
// better granularity, or you could switch to setting the major
// interval instead, however that approach would require a bit
// more checking to make sure the control has sane settings.
public class MajorMinorTimer
{
// this sets the interval in seconds between the
// "minor" ticks used for intermediate processing
// these are the "inner" intervals of the timer
private int myMinorTickInterval_sec;
public int MinorTickInterval_sec
{
get { return myMinorTickInterval_sec; }
}
// this sets the number of minor ticks between the
// expiration of the major interval of the timer.
// the "outer" interval of the timer
private int myMinorTicksPerMajorTick;
public int MinorTicksPerMajorTick
{
get { return myMinorTicksPerMajorTick; }
}
public MajorMinorTimer
(
int parMinorTickInterval_sec,
int parMinorTicksPerMajorTick
)
{
MinorTickInterval_sec = parMinorTickInterval_sec;
MinorTicksPerMajorTick = parMinorTicksPerMajorTick;
}
private DispatcherTimer myBackingTimer;
private int myMinorTickCount;
public void Start()
{
// reset the minor tick count and start the dispatcher
// timer with some reasonable defaults.
myMinorTickCount = 0;
myBackingTimer =
new DispatcherTimer
(
TimeSpan.FromSeconds(MinorTickInterval_sec),
DispatcherPriority.Normal,
new EventHandler(myBackingTimer_Tick),
Dispatcher.CurrentDispatcher
);
myBackingTimer.Start();
}
public event delegateMajorMinorTimerTick onTick;
private bool FireMajorMinorTimerTick(int TimeRemaining_sec)
{
// allows the timer sink to cancel the timer after this
// call; just as an idea, also could be handled with a
// call to Stop() during the event, but this
// simplifies handling a bit (at least to my tastes)
bool CancelTimer = false;
if (onTick != null)
onTick(TimeRemaining_sec, ref CancelTimer);
return CancelTimer;
}
private void myBackingTimer_Tick(object Sender, EventArgs e)
{
// since we are using a DispatchTimer with settings that should
// do not suggest the possibility of synchronization issues,
// we do not provide further thread safety. this could be
// accomplished in the future if necessary with a lock() call or
// Mutex, among other methods.
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
bool Cancel =
FireMajorMinorTimerTick(TicksRemaining * MinorTickInterval_sec);
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
public void Stop()
{
myBackingTimer.Stop();
}
}
тогда, предполагая, скажем, элемент управления Quiz, таймер используется примерно так:
public void QuestionTimerSetup()
{
// sets up a timer to fire a minor tick every second
// with a major interval of 5 seconds
MajorMinorTimer timerQuestion = new MajorMinorTimer(1, 5);
timerQuestion.onTick +=
new delegateMajorMinorTimerTick(QuestionControl_QuestionTimerTick);
}
// ...
public void QuestionControl_OnTick(int TimeRemaining_sec, ref bool CancelTimer)
{
if (TimeRemaining_sec > 0)
{
tblockQuizStatus.Text =
string.Format("There are {0} seconds remaining.", TimeRemaining_sec);
}
else
{
// just for an example
if (NoMoreQuestions)
{
CancelTimer = true;
HandleEndOfQuiz();
tblockQuizStatus.Text =
"Time's up! The quiz is complete!";
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to the next problem.";
}
}
}
другой вариант (вместо или в дополнение к событиям) при реализации этого может заключаться в добавлении Action
с учетом оставшегося времени в главном интервале для действия с малым интервалом, Action
для действия с основным интервалом и Func<bool>
, который проверяет условие останова, позволяя пользователю выполнять желаемые действия таким образом. как это:
public class MajorMinorTimer
{
public MajorMinorTimer
(
int parMinorTimerInterval_sec,
int parMinorTicksPerMajorTick,
Action<int> parMinorTickAction,
Action parMajorTickAction,
Func<bool> parShouldStopFunc
)
{
myMinorTimerInterval_sec = parMinorTimerInterval_sec;
myMinorTicksPerMajorTick = parMinorTicksPerMajorTick;
myMinorTickAction = parMinorTickAction;
myMajorTickAction = parMajorTickAction;
myShouldStopFunc = parShouldStopFunc;
}
private Action<int> myMinorTickAction;
private Action myMajorTickAction;
private Func<bool> myShouldStopFunc;
private void myBackingTimer_OnTick()
{
++myMinorTickCount;
int TicksRemaining = myMinorTickCount - MinorTicksPerMajorTick;
if (TicksRemaining == 0)
myMajorTickAction();
else
myMinorTickAction(TicksRemaining * MinorTickInterval_sec);
bool Cancel = myShouldStopFunc();
if (TicksRemaining == 0)
myMinorTickCount = 0;
if (Cancel)
Stop();
}
}
и затем в коде викторины вместо подключения события сделайте что-то вроде:
public void QuestionTimerSetup()
{
MajorMinorTimer timerQuestion =
new MajorMinorTimer
(
1,
5,
// major interval action
(SecsRemaining) =>
{
tblockQuizStatus.Text =
string.Format
(
"There are {0} seconds remaining.", SecsRemaining
);
},
// minor interval action
() =>
{
if (NoMoreQuestions)
{
tblockQuizStatus.Text =
"Time's up! This completes the quiz!";
HandleEndOfQuiz();
}
else
{
tblockQuizStatus.Text =
"Time's up! Press Enter to continue to next question.";
}
},
// timer cancel check function
() =>
IsEndOfQuizHandled()
);
}