[WPF] DispatcherTimer MVVM 패턴으로 사용하기

[WPF] DispatcherTimer MVVM 패턴으로 사용하기

이번 글에서는 DispatcherTimer에 대해 알아보겠습니다. DispatcherTimer는 간단한 수준의 주기적인 작업을 할 때 사용하는 타이머입니다.

WPF에서 Thread, Task를 사용하면 주기적인 작업을 할 수 있지만 간단한 수준의 반복 작업이나, UI만 갱신하는 경우에는 Thread, Task를 사용하는 것보다 DispatcherTimer를 사용하는 것이 더 효율적 입니다. 이전 글에서도 Thread나 Task를 사용하여 UI를 갱신할 때 Dispatcher에 Post나 Invoke 함수를 사용한 것을 볼 수 있습니다.

간단하게 사용할 수 있는 타이머지만, 대신 오래 걸리는 작업을 타이머로 수행할 경우 UI가 멈추는 현상이 생길 수 도 있습니다. 간단한 예제를 통해 DispatcherTimer의 사용에 대해 알아보겠습니다.

개발

WPF 프로젝트를 생성하여 아래와 같이 .xaml 파일을 만들어줍니다.

<Window x:Class="DispatcherTimer01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DispatcherTimer01"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0"
                    Orientation="Horizontal">
            <TextBox Text="{Binding MillSec}"
                     Width="100"
                     Margin="10"
                     TextAlignment="Center"/>
            <Button Content="Start Timer"
                    Command="{Binding StartTimerCommand}"
                    Width="100"
                    Margin="10"/>
            <Button Content="Stop Timer"
                    Command="{Binding StopTimerCommand}"
                    Width="100"
                    Margin="10"/>
        </StackPanel>
        <ListView Grid.Row="2"
                  ItemsSource="{Binding Messages}"
                  HorizontalAlignment="Stretch"
                  VerticalAlignment="Stretch"
                  Margin="10">
        </ListView>
    </Grid>
</Window>

그 다음 MainWindowViewModel.cs를 만들어 아래와 같이 만들어 줍니다.

namespace DispatcherTimer01
{
    public class MainWindowViewModel : Notifier
    {
        public double MillSec { get; set; } = 500;

        public ObservableCollection<string> Messages { get; set; } = new ObservableCollection<string>();

        public Command StartTimerCommand { get; }
        public Command StopTimerCommand { get; }

        private DispatcherTimer _timer = new DispatcherTimer();

        public MainWindowViewModel()
        {
            StartTimerCommand = new Command(OnStartTimerCommand, OnCanStartTimerCommand);
            StopTimerCommand = new Command(OnStopTimerCommand, OnCanStopTimerCommand);
        }

        private void OnStartTimerCommand()
        {
            _timer.Interval = TimeSpan.FromMilliseconds(MillSec); // 주기
            _timer.Tick += (sender, eventargs) => // Tick
            {
                string message = DateTime.Now.ToString("HH:mm:ss.fff");
                Messages.Add(message);
            };
            _timer.Start(); // 타이머 시작
        }

        private bool OnCanStartTimerCommand()
        {
            return !_timer.IsEnabled; // timer가 실행 중인지
        }

        private void OnStopTimerCommand()
        {
            _timer.Stop(); // 타이머 정지
        }

        private bool OnCanStopTimerCommand()
        {
            return _timer.IsEnabled;
        }
    }
}

주석에도 다 써있지만 정리하면 아래와 같습니다.

  • Interval : 타이머 주기. Tick 이벤트를 발생 시킬 간격
  • Tick : Interval 마다 불러주는 이벤트. 주기적으로 하고 싶은 작업 입력
  • IsEnabled : 타이머가 실행 중이면 true, 아니면 false
  • Start : 시작 함수
  • Stop : 종료 함수

위 함수를 실행하면 아래와 같이 표시됩니다.

만약 수행 작업이 오래 걸리면 아래와 같이 UI가 멈추게 됩니다. 아래는 Tick에 입력된 함수에 Thread.Sleep(1000);를 추가했을 때 나타나는 현상입니다.


github : https://github.com/3001ssw/c_sharp/tree/main/WPF/Simple_WfpApp/DispatcherTimer01