Posted by: Zeeshan Amjad | August 23, 2013

Using Timer in MVVM


Currently I came across a problem where I am supposed to use timer in my MVVM based application. Initially i was little concern that where should i put the event handler to response the timer tick, but eventually settled up to write event handler in my ViewModel. By making timer tick event handler in ViewModel, we are not making it UI specific or even UI aware anyway. Once i settled this then rest of the exercise is very straight forward.

Let’s first do some infrastructure work. Here is my simple implementation of ICommand interface. This is not a general purpose implementation of ICommand interface, because we simply ignore the predicate and command parameter, but for this small project this is enough. You can see the more general implementation here.

Code Snippet
public class RelayCommand : ICommand
{
    private Action Function;
    private Func<bool> Predicate;

    public RelayCommand(Action Function)
    {
        this.Function = Function;
    }

    public RelayCommand(Action Function, Func<bool> Predicate)
    {
        this.Function = Function;
        this.Predicate = Predicate;
    }

    #region NotifyPropertyChanged Methods

    public bool CanExecute(object parameter)
    {
        if (this.Predicate != null)
        {
            return this.Predicate();
        }

        return true;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        if (this.Function != null)
        {
            this.Function();
        }
    }

    #endregion
}

 

Now comes the fun part, ViewModel. In our ViewModel, i introduced four properties. Two of them are ICommand type properties to start and stop the timer and one is Text property to display the current time on the screen. I also made the duration property although currently we set its value 1000 and not setting it, but it would be useful in a program where we even want to set the duration of our time tick.

The interesting part is to set the event handler in out start timer method.

Code Snippet
public void StartTimer()
{
    timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(this.Duration);
    timer.Tick += new EventHandler(TimerTick);
    timer.Start();
}

 

In our event handler, we simply set the Text to current time. Here is complete implementation of our ViewModel.

Code Snippet
public class TimerViewModel : INotifyPropertyChanged
{
    private int duration;
    private string text;
    private DispatcherTimer timer = null;

    public TimerViewModel()
    {
        this.Duration          = 1000;
        this.Text              = DateTime.Now.ToString("HH:mm:ss tt");
        this.StartTimerCommand = new RelayCommand(this.StartTimer);
        this.StopTimerCommand  = new RelayCommand(this.StopTimer);
    }

    #region Properties
    
    public int Duration
    {
        get
        {
            return this.duration;
        }
        set
        {
            this.duration = value;
            RaisePropertyChanged("Duration");
        }
    }

    public string Text
    {
        get
        {
            return this.text;
        }
        set
        {
            this.text = value;
            RaisePropertyChanged("Text");
        }
    }

    public ICommand StartTimerCommand
    {
        get;
        set;
    }

    public ICommand StopTimerCommand
    {
        get;
        set;
    }

    #endregion

    #region NotifyPropertyChanged Methods

    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

    public void StartTimer()
    {
        timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(this.Duration);
        timer.Tick += new EventHandler(TimerTick);
        timer.Start();
    }

    public void StopTimer()
    {
        if (timer != null)
        {
            timer.Stop();
            timer = null;
        }
    }

    private void TimerTick(object send, EventArgs e)
    {
        this.Text = DateTime.Now.ToString("HH:mm:ss tt");
    }
}

 

Our XAML code is very straight forward, we just do the binding with Button and TextBlock. Here is our XAML code.

Code Snippet
<Window x:Class="WpfMvvmTimer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="Timer ViewModel" Height="200" Width="300">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="45"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="5"
                   VerticalAlignment="Center" HorizontalAlignment="Center"
                   FontSize="32" FontWeight="Bold"
                   Text="{Binding Text}"/>
        <Button Grid.Column="0" Grid.Row="1" Margin="5"
                Command="{Binding StartTimerCommand}"
                Content="Start Timer"/>
        <Button Grid.Column="1" Grid.Row="1" Margin="5"
                Command="{Binding StopTimerCommand}"
                        Content="Stop Timer"/>
        </Grid>
</Window>

 

In our main, we simply create an object of our ViewMode and set the DataContext property to our view Model. Here is a complete C# code of our project

Code Snippet
namespace WpfMvvmTimer
{
    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Threading;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new TimerViewModel(); ;
        }
    }

    public class TimerViewModel : INotifyPropertyChanged
    {
        private int duration;
        private string text;
        private DispatcherTimer timer = null;

        public TimerViewModel()
        {
            this.Duration          = 1000;
            this.Text              = DateTime.Now.ToString("HH:mm:ss tt");
            this.StartTimerCommand = new RelayCommand(this.StartTimer);
            this.StopTimerCommand  = new RelayCommand(this.StopTimer);
        }

        #region Properties
        
        public int Duration
        {
            get
            {
                return this.duration;
            }
            set
            {
                this.duration = value;
                RaisePropertyChanged("Duration");
            }
        }

        public string Text
        {
            get
            {
                return this.text;
            }
            set
            {
                this.text = value;
                RaisePropertyChanged("Text");
            }
        }

        public ICommand StartTimerCommand
        {
            get;
            set;
        }

        public ICommand StopTimerCommand
        {
            get;
            set;
        }

        #endregion

        #region NotifyPropertyChanged Methods

        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        public void StartTimer()
        {
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(this.Duration);
            timer.Tick += new EventHandler(TimerTick);
            timer.Start();
        }

        public void StopTimer()
        {
            if (timer != null)
            {
                timer.Stop();
                timer = null;
            }
        }

        private void TimerTick(object send, EventArgs e)
        {
            this.Text = DateTime.Now.ToString("HH:mm:ss tt");
        }
    }

    public class RelayCommand : ICommand
    {
        private Action Function;
        private Func<bool> Predicate;

        public RelayCommand(Action Function)
        {
            this.Function = Function;
        }

        public RelayCommand(Action Function, Func<bool> Predicate)
        {
            this.Function = Function;
            this.Predicate = Predicate;
        }

        #region NotifyPropertyChanged Methods

        public bool CanExecute(object parameter)
        {
            if (this.Predicate != null)
            {
                return this.Predicate();
            }

            return true;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            if (this.Function != null)
            {
                this.Function();
            }
        }

        #endregion
    }
}

 

When we run the program it shows the current time and do nothing. But if we press the Start Timer button, then it will updating the current time every second. Here is an output of the program.

TimerViewModel

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: