Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Timer Behavior

0.00/5 (No votes)
18 Mar 2016 1  
There may be times when it would be nice to have a timer on a window. This is a behavior that when attached to a TextBlock, will display the time in minutes and seconds since the control was created.

Introduction

I wanted to put a simple timer on a window that is displayed when the system is busy. It seemed to me that this really did not belong in the ViewModel, and seemed like it should not use code-behind. The solution was this behavior.

The Dependency Property

This behaviour has really two parts.

The static part is two DependencyPropertys, a public IsEnabled DependencyProperty to enable the timer, and a private TimerBehaviour DependencyProperty to save an instance of the class.

public static readonly DependencyProperty IsEnabledProperty =
  DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
  typeof(TimerBehaviour), new UIPropertyMetadata(false, OnValueChanged));

public static bool GetIsEnabled(FrameworkElement o) { return (bool)o.GetValue(IsEnabledProperty); }
public static void SetIsEnabled(FrameworkElement o, bool value) { o.SetValue(IsEnabledProperty, value); }

private static readonly DependencyProperty TimerBehaviourProperty =
DependencyProperty.RegisterAttached("TimerBehaviour", typeof(TimerBehaviour),
typeof(TimerBehaviour), new UIPropertyMetadata(null));

private static TimerBehaviour GetTimerBehaviour(FrameworkElement o)
   { return (TimerBehaviour)o.GetValue(TimerBehaviourProperty); }
private static void SetTimerBehaviour(FrameworkElement o, TimerBehaviour value)
   { o.SetValue(TimerBehaviourProperty, value); }

private static void OnValueChanged(DependencyObject dependencyObject,
  DependencyPropertyChangedEventArgs e)
{
 var frameworkElement = dependencyObject as FrameworkElement;
 if (frameworkElement == null) return;
 if (e.NewValue is bool && (bool)e.NewValue)
 {
  SetTimerBehaviour(frameworkElement, new TimerBehaviour(frameworkElement));
 }
 else
 {
  GetTimerBehaviour(frameworkElement).Dispose();
  SetTimerBehaviour(frameworkElement, null);
 }
}

Only the IsEnabled DependencyProperty has an event handler which creates a new instance of the TimerBehaviour class and saves it in the TimerBehaviour DependencyProperty when IsEnabled is set to true.

The non-static part of the class has a constructor that saves information about the FrameworkElement to which this DependencyProperty is attached, creates a new DispatcherTimer instance that will trigger every second and call the event handler, SecondEvent, and saves the current time, which is used to determine the elapsed time. It then starts the DispatcherTimer.

private readonly DateTime _startTime;
 private readonly FrameworkElement _frameworkElement;
 private readonly DispatcherTimer _timer;

 public TimerBehaviour(FrameworkElement frameworkElement)
 {
  _frameworkElement = frameworkElement;
  _startTime = DateTime.Now;
  _timer = new DispatcherTimer(new TimeSpan(0, 0, 1), DispatcherPriority.Render,
           SecondEvent, frameworkElement.Dispatcher);
  _timer.Start();
  SecondEvent(_timer, new EventArgs());
 }

 private void SecondEvent(object sender, EventArgs e)
 {
  var timerString = DateTime.Now.Subtract(_startTime).ToString("mm\\:ss");
  if (_frameworkElement is TextBlock)
   ((TextBlock)_frameworkElement).Text = timerString;
 }

The SecondEvent event handler then subtracts the saved time from the current time, and, if the FrameworkElement is a TextBlock, will set the Text property of the TextBlock to the elapsed time in minutes and seconds.

Using the Code

All that needs to be done is to include a TextBlock in the XAML file, and then include the TimerBehaviour and set its IsEnabled property to true:

<TextBlock local:TimerBehaviour.IsEnabled="True" />

Conclusion

This behaviour allows a timer to easily be added to any WPF form by only adding a TextBox and attaching this behaviour.

I can easily see where this can be enhanced, and also see where it might be modified to display just the time of day, which actually may be more useful considering that there is more need to display the time than just a timer. I could also add the ability to reset the timer, or allow flexibility of display.

History

  • 03/18/2016: Initial version
  • 03/22/2016: Updated code to show zero time when initialized
  • 09/13/2017: Fixed bugs when cursor is at last position

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here