WinUI3でポモドーロタイマーを作ってみよう！#3

# はじめに

#2で(下記)、設定画面を実装した続きから行う。(全3記事を予定)

https://qiita.com/shisojuice/items/cfe9bdeac44df851c82b

https://qiita.com/shisojuice/items/d4457bab83ab2a9e3e1c

# プロジェクトの作成(#2の続きから)

#タイマー画面の実装

そもそも、ポモドーロタイマー(テクニック)とは(下記URLより)

```
ポモドーロテクニックは、仕事を 25 分ずつのセッションに分け、そのあいだに短い休憩をはさんで行う時間管理術です。集中力を維持しながら生産性を上げる効果があるこの手法は、ビジネスシーンだけでなく、勉強などに集中したいときにも活用できるテクニックです。
```
https://asana.com/ja/resources/pomodoro-technique

まず、画面を作成する

```xaml:TimerPage.xaml
    <Grid>
        <Ellipse Fill="#FF4CAF50"  Width="300" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock x:Name="TimerTextBlock" FontSize="72" FontWeight="Bold" TextAlignment="Center" Foreground="White" Margin="0,0,0,20"/>
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                <Button x:Name="StartButton" Content="Start" Click="StartButton_Click" Margin="5" Style="{StaticResource AccentButtonStyle}" CornerRadius="10" Padding="15,5"/>
                <Button x:Name="ResetButton" Content="Reset" Click="ResetButton_Click" Margin="5" Style="{StaticResource DefaultButtonStyle}" CornerRadius="10" Padding="15,5"/>
            </StackPanel>
            <TextBlock x:Name="StatusTextBlock"  HorizontalAlignment="Center" Margin="0,20,0,0" Foreground="WhiteSmoke"/>
        </StackPanel>
    </Grid>

```

次に、タイマーのロジックを実装する

```csharp:TimerPage.xaml.cs
        private DispatcherTimer timer;
        private TimeSpan remainingTime;
        private bool isRunning = false;
        private int pomodoroCount = 0;
        private int pomodoroPhase = 4;
        private double pomodoroMinutes = 25;

        public TimerPage()
        {
            this.InitializeComponent();
            remainingTime = TimeSpan.FromMinutes(pomodoroMinutes);
            UpdateTimerDisplay();

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += Timer_Tick;
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (!isRunning)
            {
                timer.Start();
                isRunning = true;
                StartButton.Content = "Pause";
            }
            else
            {
                timer.Stop();
                isRunning = false;
                StartButton.Content = "Start";
            }
        }

        private void ResetButton_Click(object sender, RoutedEventArgs e)
        {
            timer.Stop();
            isRunning = false;
            StartButton.Content = "Start";
            remainingTime = TimeSpan.FromMinutes(pomodoroMinutes);
            pomodoroCount = 0;
            UpdateTimerDisplay();
        }

        private void Timer_Tick(object sender, object e)
        {
            remainingTime = remainingTime.Subtract(TimeSpan.FromSeconds(1));
            UpdateTimerDisplay();

            if (remainingTime == TimeSpan.Zero)
            {
                timer.Stop();
                isRunning = false;
                StartButton.Content = "Start";
                pomodoroCount++;

                string message = (pomodoroCount % pomodoroPhase == 0) ? "Long break" : "Short break";
                ShowNotification(message);

                remainingTime = (pomodoroCount % pomodoroPhase == 0) ? TimeSpan.FromMinutes(20) : TimeSpan.FromMinutes(5);
                UpdateTimerDisplay();
            }
        }

        private void UpdateTimerDisplay()
        {
            TimerTextBlock.Text = remainingTime.ToString(@"mm\:ss");
            if (isRunning)
            {
                StatusTextBlock.Text = $"Pomodoro {pomodoroCount + 1} / {pomodoroPhase} in progress";
            }
            else
            {
                if (pomodoroCount % pomodoroPhase == 0 && pomodoroCount > 0)
                {
                    StatusTextBlock.Text = "Long break";
                }
                else if (pomodoroCount > 0)
                {
                    StatusTextBlock.Text = "Short break";
                }
                else
                {
                    StatusTextBlock.Text = "Waiting to Start";
                }
            }
        }

        private async void ShowNotification(string message)
        {
            ContentDialog dialog = new ContentDialog
            {
                Title = message,
                CloseButtonText = "OK",
                XamlRoot = this.Content.XamlRoot
            };
            await dialog.ShowAsync();
        }
    

```

これだけだと寂しいので

タイマーのダイアログが起動している間、効果音を鳴らし続けるロジックも追加する

((著作権フリー)効果音が下記からDLいたしました。)

https://soundeffect-lab.info/sound/various/various3.html

```csharp:TimerPage.xaml.cs
 private async void ShowNotification(string message)
 {
     ContentDialog dialog = new ContentDialog
     {
         Title = message,
         CloseButtonText = "OK",
         XamlRoot = this.Content.XamlRoot
     };
     MediaPlayer player = new MediaPlayer();
     StorageFolder installedLocation = Package.Current.InstalledLocation;
     StorageFile file = await StorageFile.GetFileFromPathAsync(System.IO.Path.Combine(installedLocation.Path, "Assets\\alert.mp3"));
     player.Source = MediaSource.CreateFromStorageFile(file);
     player.IsLoopingEnabled = true;
     player.Play();

     await dialog.ShowAsync();

     player.Pause();
     player.Dispose();
 }
```

![WinUIWinUI3はじめてみよう3_0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/ef66ef3c-aa9e-588c-7d49-24732d2b687a.jpeg)

これでタイマー画面の実装完成！

#Todoリスト画面の実装

画面を作成する

```xaml:TaskPage.xaml
  <StackPanel Orientation="Vertical" Margin="20">
        <TextBox x:Name="NewTodoTextBox" PlaceholderText="New Todo" />
        <Button x:Name="AddButton" Content="Add" Click="AddButton_Click" Margin="0,5,0,0" />

        <ListView ItemsSource="{x:Bind todos}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
                        <TextBlock Text="{Binding}" VerticalAlignment="Center" />
                        <Button Margin="10,0,0,0" Content="Delete" Click="DeleteButton_Click" />
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

    </StackPanel>

```

Todoリストでもサウンド設定と同じくローカルのストレージを利用して

Todoリストをメモ上に保存するロジックを実装する

```csharp:TaskPage.xaml.cs
        private const string StorageFileName = "todos.txt";
        private ObservableCollection<string> todos = new ObservableCollection<string>();

        public TaskPage()
        {
            this.InitializeComponent();
            Loaded += TaskPage_Loaded;
        }
        
        private async void TaskPage_Loaded(object sender, RoutedEventArgs e)
        {
            await LoadTodosAsync();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);
        }

        private async Task LoadTodosAsync()
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            try
            {
                StorageFile file = await localFolder.GetFileAsync(StorageFileName);
                string content = await FileIO.ReadTextAsync(file);
                string[] lines = content.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
                todos.Clear();
                foreach (string line in lines)
                {
                    todos.Add(line);
                }
            }
            catch (FileNotFoundException)
            {
                //NOT EXIST
                StorageFile file = await localFolder.CreateFileAsync(StorageFileName, CreationCollisionOption.ReplaceExisting);
                await FileIO.WriteTextAsync(file, "");
            }
        }

        private async Task SaveTodosAsync()
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            StorageFile file = await localFolder.CreateFileAsync(StorageFileName, CreationCollisionOption.ReplaceExisting);
            string content = string.Join(Environment.NewLine, todos);
            await FileIO.WriteTextAsync(file, content);
        }

        private async void AddButton_Click(object sender, RoutedEventArgs e)
        {
            string newTodo = NewTodoTextBox.Text.Trim();
            if (!string.IsNullOrEmpty(newTodo))
            {
                todos.Add(newTodo);
                NewTodoTextBox.Text = "";
                await SaveTodosAsync();
            }
        }

        private async void DeleteButton_Click(object sender, RoutedEventArgs e)
        {
            if (sender is Button button && button.DataContext is string todo)
            {
                todos.Remove(todo);
                await SaveTodosAsync();
            }
        }

```

![WinUIWinUI3はじめてみよう3_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/163ba520-8086-60b6-5eeb-f506ae4639b8.jpeg)

これでTodoリスト画面の実装完成！

これで全画面の実装が完了したのでアプリケーション完成！！！

#最後に

アプリケーションをデバッグ環境ではなく、リリースして使う方法を紹介する。

プロジェクトを右クリックでパッケージ化して公開するをクリック。

アプリ　パッケージの作成を選択する。

サイドローディングを選択し次へ。

![WinUIWinUI3はじめてみよう3_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/e485e683-41e8-db81-5e67-d7e430a5c72a.jpeg)

証明書を作成するを選択し、作成ボタンを押下する

自己署名の欄に発行者共通名と適当なパスワードを入れてOK押下

![WinUIWinUI3はじめてみよう3_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/e9b62f2c-cb7b-8e2c-e73d-1d85e077395c.jpeg)

![WinUIWinUI3はじめてみよう3_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/37c5afc6-3a9f-3a06-3673-37690b80f8f7.jpeg)

信頼するボタンを押下して次へ、

パッケージの選択と構成で任意の選択をして次へ、

更新設定も任意のパスをいれて作成する。

![WinUIWinUI3はじめてみよう3_5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/3a0306a8-04f8-0fe3-8df2-b299199245dc.jpeg)

すると、成果物の出力先フォルダのパスが表示される

上記のパスにある成果物内のインストーラーをクリックするとリリース版がインストールされる。

![WinUIWinUI3はじめてみよう3_6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/35041253-e3c6-e6ef-2a1d-f9575bc207b6.jpeg)

![WinUIWinUI3はじめてみよう3_7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3777149/a70ab1b6-0b21-669e-bb7a-9114c8106a10.jpeg)

# 今回の成果物

ソース

https://github.com/shisojuice/winui_PomodoroTimer

# まとめ

全3記事でポモドーロタイマーのアプリケーションをWinUI3で実装してみた。

WinFormsと比べると情報が少なく、苦労する箇所もあったが、

Win 3 GalleryやMS公式のリファレンスを参照してどうにかなった👍

今後も学習していきたい。📝