びおりんのブログ

きままにアプリのできるまでを公開していきます

WPFのMVVMで電卓づくり(10)

不動小数点を適用するボタン「・」を実装します

構想

MainViewにてエラー時無効となる浮動小数点ボタン「・」を実装します
MainModelにて浮動小数点ボタンに対応した内部データの更新処理を実装します
※前回までの内部データの実装方法では浮動小数点ボタンを加味することが
 難しかったため、大幅にプロパティを置き換えます

下準備

構想の※部分を先に置き換えておきます

要約すると計算に用いる内部データ2点について
double型からstring型に置き換えて浮動小数点の入力状況を
把握しやすくしています

尚、元々記入していたコードで削除した部分はコメントアウトしています

MainModelにてプロパティを置き換えます

       //// 四則演算される値
        //private double OriginalValue = 0D;
        //// 四則演算する値
        //private double AffectlValue = 0D;
        // 四則演算されるテキスト
        private string OriginalText = "0";
        // 四則演算するテキスト
        private string AffectText = "0";

MainModel.Calculate()を置き換えます

       public void Calculate(OperationKind ope)
        {
            //if (!double.TryParse(this.DisplayText, out double displayValue)
            // || this.AffectlValue != displayValue)
            //{
            // return;
            //}
            if (!this.AffectText.Equals(this.DisplayText))
            {
                return;
            }
            double originalValue, affectValue;
            if (!double.TryParse(this.OriginalText, out originalValue))
            {
                return;
            }
            if (!double.TryParse(this.AffectText, out affectValue))
            {
                return;
            }

            switch (ope) 
            {
                // 四則演算
                case OperationKind.Plus:
                    //this.OriginalValue += this.AffectlValue;
                    //this.AffectlValue = 0;
                    //this.DisplayText = this.OriginalValue.ToString();
                    originalValue += affectValue;
                    this.OriginalText = originalValue.ToString();
                    this.AffectText = "0";
                    this.DisplayText = this.OriginalText;
                    break;
                case OperationKind.Minus:
                    //this.OriginalValue -= this.AffectlValue;
                    //this.AffectlValue = 0;
                    //this.DisplayText = this.OriginalValue.ToString();
                    originalValue -= affectValue;
                    this.OriginalText = originalValue.ToString();
                    this.AffectText = "0";
                    this.DisplayText = this.OriginalText;
                    break;
                case OperationKind.Multiply:
                    //this.OriginalValue *= this.AffectlValue;
                    //this.AffectlValue = 0;
                    //this.DisplayText = this.OriginalValue.ToString();
                    originalValue *= affectValue;
                    this.OriginalText = originalValue.ToString();
                    this.AffectText = "0";
                    this.DisplayText = this.OriginalText;
                    break;
                case OperationKind.Divide:
                    //if (this.AffectlValue == 0)
                    //{
                    // this.OriginalValue = 0;
                    // this.DisplayText = "0";
                    // this.ErrorText = "E";
                    //}
                    if (affectValue == 0)
                    {
                        originalValue = 0;
                        this.ErrorText = "E";
                    }
                    else
                    {
                        //this.OriginalValue /= this.AffectlValue;
                        //this.AffectlValue = 0;
                        //this.DisplayText = this.OriginalValue.ToString();
                        originalValue /= affectValue;
                    }
                    this.OriginalText = originalValue.ToString();
                    this.AffectText = "0";
                    this.DisplayText = this.OriginalText;
                    break;
                default:
                    break;
            }
        }

MainModel.Reset()を置き換えます

       public void Reset(OperationKind ope)
        {
            switch (ope)
            {
                case OperationKind.ClearAll:
                    //this.OriginalValue = 0;
                    //this.AffectlValue = 0;
                    //this.DisplayText = this.OriginalValue.ToString();
                    this.OriginalText = "0";
                    this.AffectText = "0";
                    this.DisplayText = "0";
                    this.ErrorText = string.Empty;
                    break;
                case OperationKind.Clear:
                    //if (this.IsAdditional && AffectlValue != 0)
                    //{
                    // this.AffectlValue = 0;
                    // this.DisplayText = this.AffectlValue.ToString();
                    //}
                    //else
                    //{
                    // this.IsAdditional = false;
                    // this.OriginalValue = 0;
                    // this.DisplayText = this.OriginalValue.ToString();
                    //}
                    if (this.IsAdditional)
                    {
                        if (this.AffectText.Equals("0"))
                        {
                            this.IsAdditional = false;
                            this.OriginalText = "0";
                        }
                        else
                        {
                            this.AffectText = "0";
                        }
                    }
                    else
                    {
                        this.OriginalText = "0";
                    }
                    this.DisplayText = "0";
                    this.ErrorText = string.Empty;
                    break;
            }
        }

実装

構想に沿って各MVVMクラスに追記していきます

MainView

浮動小数点ボタン「・」を実装します

     <!--小数点ボタン-->
        <Button Grid.Row="4" Grid.Column="3" Content="・" CommandParameter="Dot"/>

尚、前回までの実装で既にボタンのStyleにて
IsEnabledにエラー時falseとなるプロパティを紐づけしてあります

MainModel

クリアボタンクリックでMainViewModel.PushCommand経由で
呼び出されるMainModel.SetOperation()にて
switch-case文に浮動小数点ボタン用の処理を追記します

                   case OperationKind.Dot:
                        SetText(".");
                        break;

SetText()内の処理を書き換えます
元々記入していたコードで削除した部分はコメントアウトしています

       public void SetText(string str)
        {
            //this.DisplayText = this.IsAdditional ? AffectlValue.ToString() : OriginalValue.ToString();
            this.DisplayText = this.IsAdditional ? this.AffectText : this.OriginalText;

            if (this.DisplayText.Equals("0"))
            {               
                if (str.Equals("0") || str.Equals("00"))
                {
                    return;
                }
                if (!str.Equals("."))
                {
                    this.DisplayText = string.Empty;
                }
            }

            // すでに小数点が付与されていたら何もしない
            if (str.Equals(".") && this.DisplayText.Contains(".")) 
            {
                return;
            }

            this.DisplayText += str;

            //if (double.TryParse(this.DisplayText, out double displayValue))
            //{
            // if (this.IsAdditional)
            // {
            //     this.AffectlValue = displayValue;
            // }
            // else
            // {
            //     this.OriginalValue = displayValue;
            // }
            //}
            if (this.IsAdditional)
            {
                this.AffectText = this.DisplayText;
            }
            else
            {
                this.OriginalText = this.DisplayText;
            }
        }

動作確認

起動後「・」「1」「×」「3」「・」「=」とクリックした際の画面表示
f:id:kazoojapan1985:20200506155914p:plain

WPFのMVVMで電卓づくり(9)

計算をリセットするクリアボタン「CA」「C」を実装します
「CA」はすべてを初期化します
「C」は1回入力で現在の入力値とエラーを初期化します
「C」は2回入力で「CA」と同じ動作をします

構想

MainViewにて常時有効なクリアボタン「CA」「C」を実装します MainModelにてクリアボタンに対応したリセット処理Reset()を実装します

実装

構想に沿って各MVVMクラスに追記していきます

MainView

クリアボタン「CA」「C」を実装します

     <!--クリアボタン-->
        <Button Grid.Row="1" Grid.Column="0" Content="CA" CommandParameter="ClearAll"
                IsEnabled="True"/>
        <Button Grid.Row="5" Grid.Column="0" Content="C" CommandParameter="Clear"
                IsEnabled="True"/>

クリアボタンはIsEnable=trueに設定し、常時有効にしておきます

MainModel

クリアボタンクリックでMainViewModel.PushCommand経由で
呼び出されるMainModel.SetOperation()にて
switch-case文にクリアボタン用の処理を追記します

                   case OperationKind.ClearAll:
                        this.IsAdditional = false;
                        Reset(ope);
                        SetOperationText(ope);
                        this.Operation = OperationKind.None;
                        break;
                    case OperationKind.Clear:
                        Reset(ope);
                        SetOperationText(ope);
                        break;

Reset()を作成します

       public void Reset(OperationKind ope)
        {
            switch (ope)
            {
                case OperationKind.ClearAll:
                    this.OriginalValue = 0;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    this.ErrorText = string.Empty;
                    break;
                case OperationKind.Clear:
                    if (this.IsAdditional && AffectlValue != 0)
                    {
                        this.AffectlValue = 0;
                        this.DisplayText = this.AffectlValue.ToString();
                    }
                    else
                    {
                        this.IsAdditional = false;
                        this.OriginalValue = 0;
                        this.DisplayText = this.OriginalValue.ToString();
                    }
                    this.ErrorText = string.Empty;
                    break;
            }
        }

SetOperationText()のswitch-case文にクリアボタン用の処理を追記します

               case OperationKind.ClearAll:
                    this.OperationText = string.Empty;
                    break;
                case OperationKind.Clear:
                    if (!this.IsAdditional)
                    {
                        this.OperationText = string.Empty;
                    }
                    break;

動作確認

エラー解消

起動後「1」「÷」「0」とクリックした際の画面表示
f:id:kazoojapan1985:20200506130541p:plain 続いて「CA」もしくは「C」をクリックした際の画面表示
f:id:kazoojapan1985:20200506124342p:plain

「C」ボタンの挙動

起動後「3」「×」「4」「C」とクリックした際の画面表示
f:id:kazoojapan1985:20200506124724p:plain
続いて「5」「=」とクリックした際の画面表示
f:id:kazoojapan1985:20200506130142p:plain
起動後「3」「×」「4」「C」「C」「5」「=」とクリックした際の画面表示
f:id:kazoojapan1985:20200506130323p:plain

「CA」ボタンの挙動

起動後「3」「×」「4」「CA」「5」「=」とクリックした際の画面表示
f:id:kazoojapan1985:20200506130323p:plain

WPFのMVVMで電卓づくり(8)

計算エラーが発生したらボタンを無効化する仕組みを実装します

構想

MainModelにて計算エラーが発生した時点で表示用のErrorTextを更新し
MainViewModelにてボタンの有効無効を判定するIsEnabledをfalseに更新した後
MainViewModelでPropertyChangedイベントを発生させて
MainViewにおけるボタンを無効化します

実装

構想に沿って各MVVMクラスに追記していきます

MainModel

MainModelにて、エラー表示用のプロパティErrorTextを用意します

       // エラー表示テキスト
        public string ErrorText { get; private set; } = string.Empty;

MainModelにて、0除算時にErrorTextに「E」を代入するよう変更します

       public void Calculate(OperationKind ope)
        {
            if (!double.TryParse(this.DisplayText, out double displayValue)
             || this.AffectlValue != displayValue)
            {
                return;
            }
            switch (ope) 
            {
                case OperationKind.Plus:
                    this.OriginalValue += this.AffectlValue;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    break;
                case OperationKind.Minus:
                    this.OriginalValue -= this.AffectlValue;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    break;
                case OperationKind.Multiply:
                    this.OriginalValue *= this.AffectlValue;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    break;
                case OperationKind.Divide:
                    if (this.AffectlValue == 0)
                    {
                        this.OriginalValue = 0;
                        this.AffectlValue = 0;
                        this.DisplayText = "0";
                        this.ErrorText = "E";
                    }
                    else 
                    {
                        this.OriginalValue /= this.AffectlValue;
                        this.AffectlValue = 0;
                        this.DisplayText = this.OriginalValue.ToString();
                    }
                    break;
                default:
                    break;
            }
        }

MainViewModel

MainViewModelにて、エラー表示用プロパティErrorTextと
ボタンの有効無効を判定するプロパティIsEnabledを用意します

       public string ErrorText { get; set; }

        public bool IsButtonEnable { get; set; }

MainViewModel.PushCommandの末尾にて
this.ErrorTextにMainModel.ErrorTextを代入かつ
this.ErrorTextを参照してthis.IsButtonEnableを更新し
RaisePropertyChanged()でMainViewを更新します

           this.PushCommand = new DelegateCommand(
                param => 
                {
                    if (param == null) { return; }
                    var str = param.ToString();
                    if (model != null)
                    {
                        if (int.TryParse(str, out int num))
                        {
                            model.SetText(str);
                        }
                        else
                        {
                            model.SetOperation(str);
                        }
                        this.DisplayText = model.DisplayText;
                        RaisePropertyChanged(nameof(this.DisplayText));
                        this.ErrorText = model.ErrorText;
                        RaisePropertyChanged(nameof(this.ErrorText));
                        this.IsButtonEnable = string.IsNullOrEmpty(this.ErrorText) ? true : false;
                        RaisePropertyChanged(nameof(this.IsButtonEnable));
                        this.OperationText = model.OperationText;
                        RaisePropertyChanged(nameof(this.OperationText));
                    }
                },

MainView

四則演算子表示用の枠の上に、エラー表示用の枠を作り、ErrorTextを紐づけします

     <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <!--エラー表示-->
            <TextBlock Text="{Binding Path=ErrorText}"/>
            <!--四則演算子表示-->
            <TextBlock Grid.Row="1" Text="{Binding Path=OperationText}"/>
        </Grid>

ButtonのStyleにてボタンの有効無効を設定するIsEnabledにIsButtonEnableを紐づけします

     <!--ボタンの様式-->
        <Style TargetType="Button">
            <Setter Property="FontSize" Value="40"/>
            <Setter Property="FontStyle" Value="Normal"/>
            <Setter Property="Background" Value="Black"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontFamily" Value="arial"/>
            <Setter Property="Command" Value="{Binding Path=PushCommand}"/>
            <Setter Property="IsEnabled" Value="{Binding Path=IsButtonEnable}"/>
        </Style>

動作確認

起動後「1」「÷」「0」とクリックした際の画面表示
f:id:kazoojapan1985:20200505204541p:plain

WPFのMVVMで電卓づくり(7)

四則演算子ボタンを押すと計算が行われる仕組みを実装します

今回は操作状況を分かりやすくするため出力画面の隣に四則演算子を表示させる枠を作ります

構想

MainModelにて計算に用いる四則演算子が決定した時点で表示用のOperationTextを更新します

実装

構想に沿って各MVVMクラスに追記していきます

MainModel

MainModelにて、四則演算子表示用のプロパティOperationTextを用意します

       // 計算に用いる四則演算子表示テキスト
        public string OperationText { get; private set; } = string.Empty;

MainModelにて、四則演算子表示用のプロパティを更新するメソッドを用意します

       public void SetOperationText(OperationKind ope)
        {
            switch (ope) 
            {
                case OperationKind.Equal:
                    this.OperationText = "=";
                    break;
                case OperationKind.Plus:
                    this.OperationText = "+";
                    break;
                case OperationKind.Minus:
                    this.OperationText = "-";
                    break;
                case OperationKind.Multiply:
                    this.OperationText = "×";
                    break;
                case OperationKind.Divide:
                    this.OperationText = "÷";
                    break;
                default:
                    break;
            }
        }

MainModelにて、上記メソッドをSetOperation()にて呼び出します

       public void SetOperation(string str) 
        {
            double displayValue;
            if (!double.TryParse(this.DisplayText, out displayValue)) { return; }

            if (Enum.TryParse(str, out OperationKind ope))
            {
                switch (ope)
                {
                    case OperationKind.Equal:
                        if (this.IsAdditional)
                        {
                            Calculate(this.Operation);
                            this.IsAdditional = false;
                        }
                        SetOperationText(ope);
                        this.Operation = OperationKind.None;
                        break;
                    case OperationKind.Plus:
                    case OperationKind.Minus:
                    case OperationKind.Multiply:
                    case OperationKind.Divide:
                        if (this.IsAdditional)
                        {
                            Calculate(this.Operation);
                        }
                        else
                        {
                            this.IsAdditional = true;
                        }
                        SetOperationText(ope);
                        this.Operation = ope;
                        break;
                    default:
                        break;
                }
            }
        }

MainViewModel

MainViewModelにて、四則演算子表示用プロパティを用意します

       public string OperationText { get; set; }

MainViewModelにて、this.OperationTextにMainModel.OperationTextを代入し
RaisePropertyChanged()でMainViewを更新します

           this.PushCommand = new DelegateCommand(
                param => 
                {
                    if (param == null) { return; }
                    var str = param.ToString();
                    if (model != null)
                    {
                        if (int.TryParse(str, out int num))
                        {
                            model.SetText(str);
                        }
                        else
                        {
                            model.SetOperation(str);
                        }
                        this.DisplayText = model.DisplayText;
                        RaisePropertyChanged(nameof(this.DisplayText));
                        this.OperationText = model.OperationText;
                        RaisePropertyChanged(nameof(this.OperationText));
                    }
                },

MainView

出力画面の枠を1マス割いて、四則演算表示用の枠を作り、OperationTextを紐づけします

     <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <!--エラー表示-->
            <TextBlock Text="{Binding Path=ErrorText}"/>
            <!--四則演算子表示-->
            <TextBlock Grid.Row="1" Text="{Binding Path=OperationText}"/>
        </Grid>
        <!--出力画面-->
        <TextBlock Grid.Column="1" Grid.ColumnSpan="5"
                   Text="{Binding Path=DisplayText}" FontSize="80"/>

動作確認

起動後「1」「+」とクリックした際の画面表示
f:id:kazoojapan1985:20200505200904p:plain
続いて「2」「×」とクリックした際の画面表示
f:id:kazoojapan1985:20200505201114p:plain
続いて「3」「=」とクリックした際の画面表示
f:id:kazoojapan1985:20200505201213p:plain

WPFのMVVMで電卓づくり(6)

四則演算子ボタンを押すと計算が行われる仕組みを実装します

今回は大筋で四則演算ができるところまで作成します

構想

四則演算子ボタンクリックによる処理の流れについては、下記で構想してます

View=>ViewModel=>Model=>ViewModel=>Model

  1. MainView:四則演算子ボタンにPushCommandを紐づけします

  2. MainViewModel:四則演算子ボタンクリック時にMainModelの計算処理を呼び出します

  3. MainModel:呼び出した計算処理で計算に必要なプロパティを更新します

  4. MainViewModel:計算結果をMainViewに紐づけしたプロパティに代入し画面更新させます

  5. MainView:MainViewModelの更新を受けて計算結果を出力画面に表示させます

下準備

数字以外のボタンをクリックした際の操作の種類(四則演算等)を
取りまとめたOperationKindを適当な名前空間に定義します

   enum OperationKind
    {
        None = 0,
        Equal,
        Plus,
        Minus,
        Multiply,
        Divide,
        Clear,
        ClearAll,
        Reverse,
        GrandTotal,
        Persent,
    }

MainModelに計算に必要なプロパティを追加します

       // 四則演算される値
        private double OriginalValue = 0D;
        // 四則演算する値
        private double AffectlValue = 0D;
        // 出力画面表示テキスト
        public string DisplayText { get; private set; } = "0";
        // 操作の種類(四則演算等)
        public OperationKind Operation { get; private set; } = OperationKind.None;
        // 四則演算する値を現在取り扱っているかどうかのフラグ
        public bool IsAdditional { get; set; }

MainModel.SetText()で計算に必要な値が更新されるように変更します

       public void SetText(string str)
        {
            this.DisplayText = this.IsAdditional ? AffectlValue.ToString() : OriginalValue.ToString();

            if (this.DisplayText.Equals("0"))
            {
                if (str.Equals("0") || str.Equals("00"))
                {
                    return;
                }
                this.DisplayText = string.Empty;
            }

            this.DisplayText += str;

            if (double.TryParse(this.DisplayText, out double displayValue))
            {
                if (this.IsAdditional)
                {
                    this.AffectlValue = displayValue;
                }
                else
                {
                    this.OriginalValue = displayValue;
                }
            }
        }

実装

構想に沿って各MVVMクラスに追記していきます

1. MainView

四則演算子ボタンにPushCommandを紐づけします

     <!--ボタンの様式-->
        <Style TargetType="Button">
            <Setter Property="FontSize" Value="40"/>
            <Setter Property="FontStyle" Value="Normal"/>
            <Setter Property="Background" Value="Black"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontFamily" Value="arial"/>
            <Setter Property="Command" Value="{Binding Path=PushCommand}"/>
        </Style>

ボタンの数が増えてきましたので実装を少しでも楽にするため
ボタンのStyleにPushCommandの紐づけを記述して様式化しました

     <!--四則演算ボタン-->
        <Button Grid.Row="3" Grid.Column="4" Content="+" CommandParameter="Plus"
                Grid.RowSpan="2"/>
        <Button Grid.Row="3" Grid.Column="5" Content="-" CommandParameter="Minus"/>
        <Button Grid.Row="2" Grid.Column="4" Content="×" CommandParameter="Multiply"/>
        <Button Grid.Row="2" Grid.Column="5" Content="÷" CommandParameter="Divide"/>
        <Button Grid.Row="5" Grid.Column="6" Content="=" CommandParameter="Equal"/>

四則演算子ボタンには、個別でCommandParameterを追加しました

2. MainViewModel

四則演算子ボタンクリック時にMainModelの計算処理を呼び出します

       public MainViewModel()
        {
            var model = new MainModel();

            this.DisplayText = model.DisplayText;
            RaisePropertyChanged(nameof(this.DisplayText));

            this.PushCommand = new DelegateCommand(
                param => 
                {
                    if (param == null) { return; }
                    var str = param.ToString();
                    if (model != null)
                    {
                        if (int.TryParse(str, out int num))
                        {
                            model.SetText(str);
                        }
                        else
                        {
                            model.SetOperation(str);
                        }
                        this.DisplayText = model.DisplayText;
                        RaisePropertyChanged(nameof(this.DisplayText));
                    }
                },
                param =>
                {
                    return true;
                });
        }

計算処理はmodel.SetOperation()としました
四則演算子ボタンクリック時の引数paramは
数字でないため計算処理に到達します

       public void RaisePropertyChanged(string name) 
        {
            var args = new PropertyChangedEventArgs(name);
            PropertyChanged?.Invoke(this, args);
        }

3. MainModel

呼び出した計算処理で計算に必要なプロパティを更新します

       public void SetOperation(string str) 
        {
            double displayValue;
            if (!double.TryParse(this.DisplayText, out displayValue)) { return; }

            if (Enum.TryParse(str, out OperationKind ope))
            {
                switch (ope)
                {
                    case OperationKind.Equal:
                        if (this.IsAdditional)
                        {
                            Calculate(this.Operation);
                            this.IsAdditional = false;
                        }
                        this.Operation = OperationKind.None;
                        break;
                    case OperationKind.Plus:
                    case OperationKind.Minus:
                    case OperationKind.Multiply:
                    case OperationKind.Divide:
                        if (this.IsAdditional)
                        {
                            Calculate(this.Operation);
                        }
                        else
                        {
                            this.IsAdditional = true;
                        }
                        this.Operation = ope;
                        break;
                    default:
                        break;
                }
            }
        }

        public void Calculate(OperationKind ope)
        {
            if (!double.TryParse(this.DisplayText, out double displayValue)
             || this.AffectlValue != displayValue)
            {
                return;
            }
            switch (ope) 
            {
                case OperationKind.Plus:
                    this.OriginalValue += this.AffectlValue;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    break;
                case OperationKind.Minus:
                    this.OriginalValue -= this.AffectlValue;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    break;
                case OperationKind.Multiply:
                    this.OriginalValue *= this.AffectlValue;
                    this.AffectlValue = 0;
                    this.DisplayText = this.OriginalValue.ToString();
                    break;
                case OperationKind.Divide:
                    if (this.AffectlValue == 0)
                    {
                        this.OriginalValue = 0;
                        this.AffectlValue = 0;
                        this.DisplayText = "E";
                    }
                    else 
                    {
                        this.OriginalValue /= this.AffectlValue;
                        this.AffectlValue = 0;
                        this.DisplayText = this.OriginalValue.ToString();
                    }
                    break;
                default:
                    break;
            }
        }

0除算のときだけエラーとして出力画面に「E」を表示させるようにしました

4. MainViewModel

計算結果をMainViewに紐づけしたプロパティに代入し画面更新させます

       public void RaisePropertyChanged(string name) 
        {
            var args = new PropertyChangedEventArgs(name);
            PropertyChanged?.Invoke(this, args);
        }

MainViewと紐づけるプロパティが今後増えることを見越して
MainView上のプロパティ更新の手続きをメソッド化しました

5. MainView

MainViewModelの更新を受けて計算結果を出力画面に表示させます
特に今回追記はないので動作確認結果を掲載します

起動後「1」「+」「2」「×」「3」「=」とした場合の画面表示
f:id:kazoojapan1985:20200505022309p:plain

起動後「1」「÷」「0」「=」とした場合の画面表示
f:id:kazoojapan1985:20200505022604p:plain

今後の更新

実装の全体像を説明するのが負担になってきたので
Git環境でも構築して途中経過を載せていくようにしようと考えています

記事では、要点だけまとめるような形で模索してます

WPFのMVVMで電卓づくり(5)

電卓の数字ボタンを押したら出力画面が更新される仕組みを作成します

今回は数字ボタンクリックで更新された内部データを
電卓の出力画面に反映させます

内部データの出力画面へのバトンタッチ(Model⇒ViewModel⇒View)

数字ボタンクリックで更新されたMainModel.DisplayTextを
MainViewModel.DisplayTextに代入します
MainViewModel.DisplayTextをRaisePropertyChangedイベントで
MainView.xaml上の出力画面であるTextBlockに反映させます

Model⇒ViewModel

MainViewModel.PushCommand.Execute()に登録したラムダ式にて
MainModel.SetText()を呼び出して更新したMainModel.DisplayTextを
MainViewModel.DeisplayTextに代入する処理を追加します

           this.PushCommand = new DelegateCommand(
                param => 
                {
                    if (param == null) { return; }
                    var str = param.ToString();
                    if (int.TryParse(str, out int num))
                    {
                        model.SetText(str);
                    }
                    this.DisplayText = model.DisplayText;
                },
                param =>
                {
                    return true;
                });

ViewModel⇒View

MainViewModelにINotifyPropertyChangedインターフェースを実装し
MainModel.DisplayText代入後のMainViewModel.DisplayTextを引数にして
RaisePropertyChangedイベントを着火します

   class MainViewModel : INotifyPropertyChanged
    {
        public string DisplayText { get; set; }

        public ICommand PushCommand { get; set; }
        public MainViewModel()
        {
            var model = new MainModel();
            this.PushCommand = new DelegateCommand(
                param => 
                {
                    if (param == null) { return; }
                    var str = param.ToString();
                    if (int.TryParse(str, out int num))
                    {
                        model.SetText(str);
                    }
                    this.DisplayText = model.DisplayText;
                    var args = new PropertyChangedEventArgs(nameof(this.DisplayText));
                    PropertyChanged(this, args);
                },
                param =>
                {
                    return true;
                });
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

MainView.xamlにおいて、出力画面であるTextBlockにDisplayTextを紐づけします

        <!--出力画面-->
        <TextBlock Grid.ColumnSpan="6" Text="{Binding Path=DisplayText}"/>

動作確認

数字ボタンを「1」「9」「00」の順にクリックすると
出力画面に「1900」と表示されます
f:id:kazoojapan1985:20200503181824p:plain

WPFのMVVMで電卓づくり(4)

電卓の数字ボタンを押したら出力画面が更新される仕組みを作成します

今回はボタンクリックに紐づけしたコマンドに渡されるパラメーターを用いて
電卓の出力画面用の内部データを更新します

下準備

下図はソリューションエクスプローラーの構成です
f:id:kazoojapan1985:20200503010317p:plain

前回までで下記ファイルを作成済みです

  • App.xaml
  • DelegateCommand.cs
  • MainView.xaml
  • MainViewModel.cs

今回はMainModel.csを追加していきます

電卓ボタンに登録したコマンドパラメーターを引数に
出力画面に表示させる文字列であるDisplayTextを設定する
SetText()を含むクラスをMainModel.csに作成します

   class MainModel
    {
        public string DisplayText { get; private set; }

        public void SetText(string str)
        {
            if ((str.Equals("0") || str.Equals("00"))
             && string.IsNullOrEmpty(this.DisplayText))
            {
                return;
            }
            this.DisplayText += str;
        }
    }

ボタンクリックで出力画面用の内部データを更新させる

数字ボタンをクリックすることで呼び出されるMainViewModel.PushCommandから
出力画面に表示させる文字列を格納するMainModel.SetText()を呼び出します

   class MainViewModel
    {
        public string DisplayText { get; set; }
        public ICommand PushCommand { get; set; }

        public MainViewModel()
        {
            var model = new MainModel();
            this.PushCommand = new DelegateCommand(
                param => 
                {
                    MessageBox.Show(param?.ToString() ?? string.Empty);
                    if (param == null) { return; }
                    var str = param.ToString();
                    if (int.TryParse(str, out int num))
                    {
                        model.SetText(str);
                    }
                },
                param =>
                {
                    return true;
                });
        }
    }

上のコードでは、MainViewModelのコンストラクタにてMainModelを生成し
MainViewModel.PushCommand実行時に参照できるようにしています
またPushCommand.Execute()にてMainModel.SetText()を呼び出しています

動作確認

MainModel.SetText()にブレークポイントを設定しておきます
数字ボタンをクリックするとMainModel.SetText()に到達することを確認できます
f:id:kazoojapan1985:20200503164802p:plain