Используя таймер, как запустить функцию из другого пользовательского элемента управления, когда время истекло - PullRequest
3 голосов
/ 10 августа 2011

У меня есть пользовательский элемент управления WPF с именем TimerUserControl, в котором содержится таймер.И у меня есть другой пользовательский элемент управления, где отображаются вопросы, в этом есть функция NextQuestion.

Таймер имеет интервал 2 минуты, и я хотел бы вызвать функцию NextQuestion, когда это будет сделано.Я думаю, что я должен использовать делегатов, но я не уверен.

ОБНОВЛЕНИЕ 1:

public partial class TimeUserControl : UserControl
{
    public int _totalSeconds;
    public int _secondsRemaining;
    public DispatcherTimer timerSecondsLeft;

    public TimeUserControl()
    {
        InitializeComponent();

        timerSecondsLeft = new DispatcherTimer();
        timerSecondsLeft.Tick += new EventHandler(timerSecondsLeft_Tick);
        timerSecondsLeft.Interval = new TimeSpan(0, 0, 1);
    }

    public bool TimesUp
    {
        get;
        set;
    }

    public void SetSeconds(int seconds)
    {
        timerSecondsLeft.Stop();

        if (seconds == 0)
        {
            TimeTextBlock.Text = "There's no time! Hurray";
        }
        else
        {
            _totalSeconds = seconds;
            _secondsRemaining = seconds;

            TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _totalSeconds);
            timerSecondsLeft.Start();
        }
    }

    public void timerSecondsLeft_Tick(object sender, EventArgs e)
    {
        _secondsRemaining--;

        if (_secondsRemaining <= 0)
        {
            timerSecondsLeft.Stop();

            TimesUp = true;
            TimeTextBlock.Text = "Time's up. Press Enter to next problem.";

            // HERE WILL INVOKE NEXTQUESTION FUNCTION
        }
        else
        {
            TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
        }
    }
}

Посмотрите в коде, комментарий это возможно использование делегатов?

Ответы [ 3 ]

3 голосов
/ 10 августа 2011

Так что вам нужно сделать несколько вещей. Вы должны добавить некоторый код в свой пользовательский контроль.

// Declare this outside your usercontrol class
public delegate void TimerExpiredEventHandler(object sender, EventArgs e);

Это то, что нужно добавить в ваш код для управления пользователем.

public partial class TimerUserControl : UserControl
{
    public event TimerExpiredEventHandler Expired;

    public void OnExpired(EventArgs e)
    {
        if (Expired != null)
            Expired(this, e);
    }

public void timerSecondsLeft_Tick(object sender, EventArgs e)
{
    _secondsRemaining--;

    if (_secondsRemaining <= 0)
    {
        timerSecondsLeft.Stop();

        TimesUp = true;
        TimeTextBlock.Text = "Time's up. Press Enter to next problem.";

        // Fire the event here.
        OnExpired(EventArgs.Empty);
    }
    else
    {
        TimeTextBlock.Text = string.Format("It remains {0} seconds. Don't take long!", _secondsRemaining);
    }
}

}

Теперь вам нужно подписаться на это событие внутри того, что вызывает этот пользовательский контроль.

public partial class ParentForm : Form
{
    private void ParentForm_Load(object sender, EventArgs e)
    {
         var timer = new TimerUserControl();
         //Subscribe to the expired event that we defined above.
         timer.Expired += new EventArgs(Timer_Expired);
    }

    public void Timer_Expired(object sender, EventArgs e)
    {
        //Handle the timer expiring here. Sounds like you are calling another function, so do that here.
    }
}
0 голосов
/ 10 августа 2011

Я бы, вероятно, нарушил функционал управления таймером здесь; что-то вроде этого (примечание: я пишу это на лету, поэтому дайте мне знать, если это не работает как есть, и я помогу исправить любые проблемы):

// 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()
      ); 
}
0 голосов
/ 10 августа 2011

Используйте TreeHelper для поиска дерева для общего родителя, а затем вниз по дереву для требуемого пользовательского элемента управления. Примерно такой псевдокод:

this.Timer = new System.Windows.Threading.DispatcherTimer
{
    Interval = new TimeSpan(0, 0, 1)
};
this.Timer.Tick += (s, e) =>
{
    var _Control = s as MyFirstControl;
    var _Other = LogicalTreeHelper.GetChildren(_Control.Parent)
        .Cast<FrameworkElement>().Where(x => x.Name == "FindIt")
        .First<MySecondControl>();
    _Other.DoMethod();
};

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...