WinUI3を学びたく、練習として簡単な電卓を作ってみることにしました。
今回は入力データの妥当性チェック処理のうち、ダイアログにメッセージを表示する実装を行います。
C#とxamlに関しても初心者ですのでお手柔らかにお願いします。
前回の記録
やりたいこと
入力したデータが計算式として成り立つかどうかをチェックします。
その結果、成り立たない場合はエラーメッセージをダイアログで出力します。
今回はデータのチェックまではせず、メッセージをダイアログに出力するコードまでを実装します。
エラーとする内容の洗い出し
チェック処理自体は次回にしますが、メッセージをダイアログに出力するためのエラー一覧は先に決めておきます。
エラーとなるパターンは以下のようになりそうです。
(考慮不足がでてきた場合は随時追加していくと思います)
- 式の先頭に「× ÷ . )」が出現した
- 「× ÷ .」が連続して出現した
- 「.」の後に数字以外が出現した
- 「+ - × ÷ . (」が式の末尾に出現した
- 式の先頭に「+ - (」が出現した場合、次の文字が数字、「(」だった
- 式の途中に出現した連続する「+ -」が3個以上だった
(1+-1などは問題ないが、1+--1」などは成り立たない) - カッコが対になっていない
- 数字、「. )」の次の文字が「(」だった
- 「(」の後に「× ÷ . )」が出現した
- 記号のみで構成されている
- 先頭が「(」の場合、次に文字が「+ - (」でなかった
これによりエラーコードとエラー内容を以下のようにします。
| エラーコード | エラーメッセージ |
|---|---|
| S1 | 正常な式、メッセージなし |
| E1 | 式の先頭に「× ÷ . )」が出現しました。 |
| E2 | 「×、÷、.」が連続して出現しました。 |
| E3 | 「.」の後に数字以外が出現しました。 |
| E4 | 末尾に数字、「)」以外が出現しました。 |
| E5 | 先頭が「+ - (」の場合、 次に出現できるのは数字、「(」のみです。 |
| E6 | 式の途中に連続する「+ -」が3個以上出現しました。 |
| E7 | カッコが対になっていません。 |
| E8 | 数字、「. )」の次に「(」が出現しました。 |
| E9 | 「(」の後に「× ÷ . )」が出現しました。 |
| E10 | 記号のみで構成されています。 |
| E11 | 先頭が「(」の場合、次に出現できるのは数字、「+ - (」のみです。 |
ViewModelの作成
最初にメッセージを管理するErrorViewModelをViewModelフォルダに作成します。
基本内容はInputViewModelやResultViewModelと同様です。
ErrorViewModelの初期コード
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));
}
}
ここにまずエラーコードを表現する列挙型を定義します。
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 { };
// 省略
}
次にこのコードをキーにしたメッセージマップを定義します。
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 { };
// 省略
}
次にエラーコードを格納するプロパティを定義します。
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();
+ }
+ }
// 省略
}
次にエラーメッセージを取得するプロパティを定義します。
このプロパティは取得とあるようにセッターはありません。
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.csにValidFormula関数を定義します。
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の空白のページを選択します。
最初にコードビハインド側を修正します。
ErrorViewModelを紐づけます。
namespace Calculator.View;
public partial class ErrorMessageDialogContent : Page
{
public ErrorViewModel ErrorViewModel { get; set; }
public ErrorMessageDialogContent()
{
this.InitializeComponent();
// ViewModelを初期化します。
ErrorViewModel = new ErrorViewModel();
}
}
次に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を作成します。
ダイアログの出力は結構迷走したので、後々別途解決記事を書きたいと思います。
取り合えず、実装は以下のようにしました。
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.csのEqual_Button_Clickの先頭にエラーであった場合はメッセージを出力する処理を追加します。
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に適当にエラーコードを渡す事でメッセージが表示されることを確認できました。
おわりに
今回はダイアログを表示するだけでしたが、思った以上に手間取りました。
一応できはしましたが、よくわからないコードも多々あるのでスキルアップしたら読み解いていきたいです。
次回は式のチェックを行います。
ここでいよいよ本格的にC#でロジックを書きますね。
これまた一筋縄ではいかなさそうです。
次の記事


