【C#】WinUI3＋MVVM toolkitにて、一定の周期で実行するタスクの作り方

## はじめに
__WinUI3__ と __MVVM toolkit__ を使い始めて1ヶ月ほど経ちました。（まだまだ慣れない）

この記事では、WinUI3とMVVM toolkitを組み合わせて __デスクトップアプリ__ を開発する際、どのようにして __一定の周期で実行するタスク__ を実装したかを説明します。

ちなみに、ボタンを押すとラベルのテキストが変わるアプリのMVVM実装の説明は複数みかけました。でもこれは、View-ViewModelで完結してしまうんですね。
一定の周期で実行するタスクを実装するとなると __Model層にも手を入れる必要がある__ のですが、このサンプルをなかなか見つけることができず、苦労しました。

同じように困っている誰かの一助になれば幸いです。


## この記事の主な対象者
- MVVM、MVVM toolkitに興味がある人
- WinUI3に興味がある人
- WPF,UWPを扱っている人。（MVVM toolkitを使うと同じ手順で実装できるはず）

## この記事の目標
- MVVM toolkit＋WinUI3に定期実行するタスクを作ることができるようになる。
- WinUI3やらMVVMやら敬遠してたけど、やればできるんじゃね？と思い始める。

## この記事に含めないこと
- WinUI3の説明
- MVVMの説明
- MVVM toolkitの説明

## 環境
- Microsoft Visual Studio Community 2022 (64 ビット)  Version 17.2.6

## プロジェクトを作成

まずは[Template Studio for WinUI (C#)](https://marketplace.visualstudio.com/items?itemName=TemplateStudio.TemplateStudioForWinUICs)をインストールをします。


インストール完了後、「新しいプロジェクトの作成」からTemplate Studio for WinUI (C#)を選択し、次へ>>作成を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630591/42b0a422-85aa-c885-4bb8-30aacde1722d.png)

テンプレート作成のためのナビゲーション画面が表示されます。
アプリの種類やページの数を設定できるようになっていることが確認できます。
今回は何も考えず《作成》ボタンをクリックしてプロジェクト作成を完了させましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630591/0c7bef75-4338-cf09-ccf3-3b5bb3a9f3ed.png)

## 起動確認

まずは動作することを確認しましょう。
デバッグ＞デバッグの開始を選択します。
次のように表示されればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630591/254a80fd-5e69-5a70-343a-1fb9f05c577c.png)


## 一定の周期で実行するタスクを作る

### ファイルを作成

Template Studioが生成するプログラムに倣って、2つのファイル、IIntervalServive.csとIntervalServive.csをそれぞれ次のように作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630591/14e42a2d-f3ff-9528-d521-14984de7cee1.png)

IIntervalServive.cs
``` C#
using System;

namespace YourAppName.Contracts.Services;

public interface IIntervalService
{
}

```


IntervalServive.cs
``` C#
using YourAppName.Contracts.Services;


namespace YourAppName.Services;

public class IntervalService : IIntervalService
{
    // コンストラクタ
    public IntervalService()
    {
    }
}
```

### 一定周期で実行する関数を実装

System.Timers.Timerクラスを用い、一定周期で実行する関数を実装します。
IntervalServive.csにTimerクラスのフィールドと、Run(),Stop(),Clock()メソッドを追加し、コンストラクタからRun()を実行します。

IntervalServive.cs
``` C#
using System;
using System.Diagnostics;
using System.Timers;
using App11.Contracts.Services;


namespace App11.Services;

public class IntervalService : IIntervalService
{
    Timer _timer;

    // コンストラクタ
    public IntervalService()
    {
        Run();
    }

    public void Run()
    {
        Debug.WriteLine("タイマ開始");
        _timer = new Timer();
        _timer.Elapsed += new ElapsedEventHandler(Clock);
        _timer.Interval = 1000;     // コンストラクタでも指定可
        _timer.AutoReset = true;    // デフォルト
        _timer.Enabled = true;      // timer.Start()と同じ

    }


    public void Stop()
    {
        _timer.Enabled = false;     // timer.Stop()と同じ
        _timer.Dispose();
        Debug.WriteLine("タイマ停止");
    }

    private void Clock(object sender, ElapsedEventArgs e)
    {
        Debug.WriteLine(DateTime.Now.ToString("HH:mm:ss"));
    }
}

```

### アプリケーションにサービスを追加

作成したサービスをアプリケーションにシングルトンパターンで追加します。
App.xamlのコードビハインド（App.xaml.cs）を開きます。

NavigationServiceを追加している行の下に、IntervalServiceを追加します。

``` C#
// Services
services.AddTransient<INavigationViewService, NavigationViewService>();

services.AddSingleton<IActivationService, ActivationService>();
services.AddSingleton<IPageService, PageService>();
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<IIntervalService, IntervalService>();  // この行を追加

// Core Services
services.AddSingleton<IFileService, FileService>();
```
### サービスを実行
シングルトンパターンなので、追加しただけで動作してくれるのでは？と思ってしまいますが、そうなっていないようです。
サービスを実行してあげましょう。

具体的には、ViewModelにてサービスを参照するとコンストラクタが実行されるようです。（コンストラクタが実行されるのは初回のみ）

MainviewModel.csを開き、次のように実装します。

MainviewModel.cs
``` C#
YourAppName.Contracts.Services;
using CommunityToolkit.Mvvm.ComponentModel;

namespace YourAppName.ViewModels;

public class MainViewModel : ObservableRecipient
{

    private readonly IIntervalService _intervalService;

    public MainViewModel(IIntervalService intervalService)
    {
        _intervalService = intervalService;
    }
}

```

### アプリケーションを起動
Clock()メソッドが一定の周期（1秒）ごとに実行されている様子が確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630591/fc89e748-1097-d917-2884-57288c5ddd07.png)

## 最後に
いかがだったでしょうか。

なんとか（？）MVVMを崩さずに一定周期で実行するタスクを実装できたかと思います。
ただ、MVVM Toolkitの仕組みを理解できておらず、なぜこの実装で動作するのか、まだ理解できておりません。

より適切な実装方法があるのかもしれません。
熟練者の方。ぜひアドバイスやフィードバックをお願いします。(ｏ_ _)ｏ

## 参考
- [タイマにより一定時間間隔で処理を行うには？（サーバベースタイマ編）](https://atmarkit.itmedia.co.jp/ait/articles/0511/11/news119.html) タイマの実装方法を参考にさせていただきました。