0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

WinUI3で超簡易電卓を作る練習(入力データの妥当性チェック-メッセージ出力編)

0
Last updated at Posted at 2025-01-21

WinUI3を学びたく、練習として簡単な電卓を作ってみることにしました。
今回は入力データの妥当性チェック処理のうち、ダイアログにメッセージを表示する実装を行います。

C#とxamlに関しても初心者ですのでお手柔らかにお願いします。

前回の記録

やりたいこと

入力したデータが計算式として成り立つかどうかをチェックします。
その結果、成り立たない場合はエラーメッセージをダイアログで出力します。

今回はデータのチェックまではせず、メッセージをダイアログに出力するコードまでを実装します。

エラーとする内容の洗い出し

チェック処理自体は次回にしますが、メッセージをダイアログに出力するためのエラー一覧は先に決めておきます。

エラーとなるパターンは以下のようになりそうです。
(考慮不足がでてきた場合は随時追加していくと思います)

  • 式の先頭に「× ÷ . )」が出現した
  • 「× ÷ .」が連続して出現した
  • 「.」の後に数字以外が出現した
  • 「+ - × ÷ . (」が式の末尾に出現した
  • 式の先頭に「+ - (」が出現した場合、次の文字が数字、「(」だった
  • 式の途中に出現した連続する「+ -」が3個以上だった
     (1+-1などは問題ないが、1+--1」などは成り立たない)
  • カッコが対になっていない
  • 数字、「. )」の次の文字が「(」だった
  • 「(」の後に「× ÷ . )」が出現した
  • 記号のみで構成されている
  • 先頭が「(」の場合、次に文字が「+ - (」でなかった

これによりエラーコードとエラー内容を以下のようにします。

エラーコード一覧
エラーコード エラーメッセージ
S1 正常な式、メッセージなし
E1 式の先頭に「× ÷ . )」が出現しました。
E2 「×、÷、.」が連続して出現しました。
E3 「.」の後に数字以外が出現しました。
E4 末尾に数字、「)」以外が出現しました。
E5 先頭が「+ - (」の場合、
次に出現できるのは数字、「(」のみです。
E6 式の途中に連続する「+ -」が3個以上出現しました。
E7 カッコが対になっていません。
E8 数字、「. )」の次に「(」が出現しました。
E9 「(」の後に「× ÷ . )」が出現しました。
E10 記号のみで構成されています。
E11 先頭が「(」の場合、次に出現できるのは数字、「+ - (」のみです。

ViewModelの作成

最初にメッセージを管理するErrorViewModelViewModelフォルダに作成します。
基本内容はInputViewModelResultViewModelと同様です。

ErrorViewModelの初期コード
ErrorViewModel.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Calculator.ViewModel;

public partial class ErrorViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged = delegate { };

    public void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ここにまずエラーコードを表現する列挙型を定義します。

ErrorViewModel.cs
namespace Calculator.ViewModel;

public partial class ErrorViewModel : INotifyPropertyChanged
{
+    public enum ERROR_CODE
+    {
+        S1, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11,
+    };

     public event PropertyChangedEventHandler? PropertyChanged = delegate { };

       // 省略
}

次にこのコードをキーにしたメッセージマップを定義します。

ErrorViewModel.cs
namespace Calculator.ViewModel;

public partial class ErrorViewModel : INotifyPropertyChanged
{
    public enum ERROR_CODE
    {
        S1, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11,
    };

+   private static Dictionary<ERROR_CODE, string> CodeMap = new()
+   {
+      { ERROR_CODE.S1, string.Empty },
+      { ERROR_CODE.E1, "式の先頭に「× ÷ . )」が出現しました。" },
+      { ERROR_CODE.E2, "「×、÷、.」が連続して出現しました。" },
+      { ERROR_CODE.E3, "「.」の後に数字以外が出現しました。" },
+      { ERROR_CODE.E4, "末尾に数字、「)」以外が出現しました。"},
+      { ERROR_CODE.E5, "先頭が「+、-、(」の場合、次に出現できるのは数字、「(」のみです。" },
+      { ERROR_CODE.E6, "式の途中に連続する「+、-」が3個以上出現しました。" },
+      { ERROR_CODE.E7, "カッコが対になっていません。" },
+      { ERROR_CODE.E8, "「(」の前に「.、)」、数字が出現しました。" },
+      { ERROR_CODE.E9, "「(」の後に「×、÷、.、)」が出現しました。" },
+      { ERROR_CODE.E10, "記号のみで構成されています。" },
+      { ERROR_CODE.E11, "先頭が「(」の場合、次に出現できるのは数字、「+ - (」のみです。" },
+   };

    public event PropertyChangedEventHandler? PropertyChanged = delegate { };

        // 省略
}

次にエラーコードを格納するプロパティを定義します。

ErrorViewModel.cs
namespace Calculator.ViewModel;

public partial class ErrorViewModel : INotifyPropertyChanged
{
        // 省略
   
    public event PropertyChangedEventHandler? PropertyChanged = delegate { };

+   private ERROR_CODE _code = ERROR_CODE.S1;
+   public ERROR_CODE Code
+   {
+       get { return _code; }
+       set
+       {
+           _code = value;
+           OnPropertyChanged();
+       }
+   }

      // 省略
}

次にエラーメッセージを取得するプロパティを定義します。
このプロパティは取得とあるようにセッターはありません。

ErrorViewModel.cs
namespace Calculator.ViewModel;

public partial class ErrorViewModel : INotifyPropertyChanged
{
        // 省略

    private ERROR_CODE _code = ERROR_CODE.S1;
    public ERROR_CODE Code
    {
        get { return _code; }
        set
        {
            _code = value;
            OnPropertyChanged();
        }
    }

+   public string ErrorMessage
+   {
+       get { return CodeMap[Code]; }
+   }

    public void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

チェック処理の定義

まだ処理は実装しませんが、MainWindow.xaml.csValidFormula関数を定義します。

MainWindow.xaml.cs
namespace Calculator;

public sealed partial class MainWindow : Window
{
        // 省略

    /// <summary>
    /// =ボタン押下時の処理です。
    /// 入力領域にデータが存在する場合、データを元に計算処理をし、
    /// その結果を結果領域に出力します。
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Equal_Button_Click(object sender, RoutedEventArgs e)
    {
        // 現在の入力データを取得します。
        string formula = InputViewModel.Formula;

        // 入力データが空の場合は処理をしません。
        if (formula == string.Empty)
        {
            return;
        }
        // 入力データを元に計算処理を行います。
        ResultViewModel.Calculation(formula);
    }

+   /// <summary>
+   /// 入力データが式として成り立つかを検証します。
+   /// 式として成り立たない場合、対応するエラーコードを返します。
+   /// </summary>
+   /// <returns></returns>
+   private ErrorViewModel.ERROR_CODE ValidFormula()
+   {
+       return ErrorViewModel.ERROR_CODE.S1;
+   }

       // 省略
}

ダイアログの設定

今回、ダイアログはContentDialogを使用します。

最初に表示用のxamlを作成します。
プロジェクトに「View」フォルダを作成し、その中にErrorMessageDialogContent.xamlを作成します。
この際、テンプレートはWinUI3の空白のページを選択します。

2025.01.21-1.jpg

2025.01.21-2.jpg

最初にコードビハインド側を修正します。
ErrorViewModelを紐づけます。

ErrorMessageDialogContent.cs
namespace Calculator.View;

public partial class ErrorMessageDialogContent : Page
{
    public ErrorViewModel ErrorViewModel { get; set; }

    public ErrorMessageDialogContent()
    {
        this.InitializeComponent();

        // ViewModelを初期化します。
        ErrorViewModel = new ErrorViewModel();
    }
}

次にxamlを以下コードのように修正します。

ErrorMessageDialogContent.xaml
<?xml version="1.0" encoding="utf-8"?>
<Page
    x:Class="Calculator.View.ErrorMessageDialogContent"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Calculator.View"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <TextBlock Text="{x:Bind ErrorViewModel.ErrorMessage, Mode=OneWay}"
                   TextWrapping="Wrap" />
    </StackPanel>
</Page>

TextBlockにエラーメッセージをバインドする形で実装します。

メッセージの表示処理

MainWindow.xaml.csにメッセージをContentDialogで出力する関数、ShowErrorMessageを作成します。
ダイアログの出力は結構迷走したので、後々別途解決記事を書きたいと思います。

取り合えず、実装は以下のようにしました。

MainWindow.xaml.cs
namespace Calculator;

public sealed partial class MainWindow : Window
{
         // 省略
    
    private ErrorViewModel.ERROR_CODE ValidFormula()
    {
        return ErrorViewModel.ERROR_CODE.S1;
    }

+   /// <summary>
+   /// エラーコードより対応するメッセージをダイアログに出力します。
+   /// エラーコードが正常であった場合、ダイアログを出力しません。
+   /// </summary>
+   /// <param name="errorCode"></param>
+   private async void ShowErrorMessage(ErrorViewModel.ERROR_CODE errorCode)
+   {
+       // エラーコードが正常であった場合、処理を終了します。
+       if (errorCode == ErrorViewModel.ERROR_CODE.S1)
+       {
+           return;
+       }
+
+       // 設定したメッセージをコンテントダイアログに出力します。
+       ErrorMessageDialogContent content = new();
+       content.ErrorViewModel.Code = errorCode;
+       ContentDialog dialog = new()
+       {
+           XamlRoot = this.Content.XamlRoot,
+           Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style,
+           Title = "式の解析に失敗しました",
+           CloseButtonText = "閉じる",
+           Content = content,
+       };
+
+       await dialog.ShowAsync();
+   }

    /// <summary>
    /// Cボタン押下時の処理です。
    /// 入力領域と結果領域のデータを空にします。
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Clear_Button_Click(object sender, RoutedEventArgs e)
    {
        InputViewModel.Formula = string.Empty;
        ResultViewModel.Result = string.Empty;
    }
}

MainWindow.xaml.csEqual_Button_Clickの先頭にエラーであった場合はメッセージを出力する処理を追加します。

MainWindow.xaml.cs
namespace Calculator;

public sealed partial class MainWindow : Window
{
       // 省略

    /// <summary>
    /// =ボタン押下時の処理です。
    /// 入力領域にデータが存在する場合、データを元に計算処理をし、
    /// その結果を結果領域に出力します。
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Equal_Button_Click(object sender, RoutedEventArgs e)
    {
+       // 式が成り立っているかを検証し、エラーであればメッセージを出力します。
+       ErrorViewModel.ERROR_CODE validCode = ValidFormula();
+       if (validCode != ErrorViewModel.ERROR_CODE.S1)
+       {
+           ShowErrorMessage(validCode);
+       }

        // 現在の入力データを取得します。
        string formula = InputViewModel.Formula;

        // 入力データが空の場合は処理をしません。
        if (formula == string.Empty)
        {
            return;
        }
        // 入力データを元に計算処理を行います。
        ResultViewModel.Calculation(formula);
    }

       // 省略
}

完成

ここまででエラーメッセージをダイアログで表示することができるようになりました。
ShowErrorMessageに適当にエラーコードを渡す事でメッセージが表示されることを確認できました。

2025.01.21-3.jpg

おわりに

今回はダイアログを表示するだけでしたが、思った以上に手間取りました。
一応できはしましたが、よくわからないコードも多々あるのでスキルアップしたら読み解いていきたいです。

次回は式のチェックを行います。
ここでいよいよ本格的にC#でロジックを書きますね。

これまた一筋縄ではいかなさそうです。

次の記事

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?