MVVM模式WPF計算器

採用MvvmLight工具構建的計算器


Models:CalculationModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MvvmCalculator.Models
{
    public class CalculationModel
    {
        #region 私有成員

        private string _result;

        #endregion

        #region 構造函數

        public CalculationModel()
        {
            FirstOperand = string.Empty;
            SecondOperand = string.Empty;
            Operation = string.Empty;
            _result = string.Empty;
        }

        #endregion

        #region 公共屬性和方法

        public string FirstOperand { get; set; }        //第一個數
        public string SecondOperand { get; set; }       //第二個數
        public string Operation { get; set; }           //運算符
        public string Result { get { return _result; } } //結果

        public void CalculateResult()       //計算結果
        {
            ValidateData();     //校驗數據

            try
            {
                switch (Operation)  //利用運算符,決定使用哪種算法
                {
                    case ("+"):
                        _result = (Convert.ToDouble(FirstOperand) + Convert.ToDouble(SecondOperand)).ToString();
                        break;

                    case ("-"):
                        _result = (Convert.ToDouble(FirstOperand) - Convert.ToDouble(SecondOperand)).ToString();
                        break;

                    case ("*"):
                        _result = (Convert.ToDouble(FirstOperand) * Convert.ToDouble(SecondOperand)).ToString();
                        break;

                    case ("/"):
                        _result = (Convert.ToDouble(FirstOperand) / Convert.ToDouble(SecondOperand)).ToString();
                        break;

                    case ("sin"):
                        _result = Math.Sin(DegreeToRadian(Convert.ToDouble(FirstOperand))).ToString();
                        break;

                    case ("cos"):
                        _result = Math.Cos(DegreeToRadian(Convert.ToDouble(FirstOperand))).ToString();
                        break;

                    case ("tan"):
                        _result = Math.Tan(DegreeToRadian(Convert.ToDouble(FirstOperand))).ToString();
                        break;
                }
            }
            catch (Exception)
            {
                _result = "Error whilst calculating";
                throw;
            }
        }

        private double DegreeToRadian(double angle)  //角度運算
        {
            return Math.PI * angle / 180.0;
        }

        private void ValidateOperand(string operand)    //校驗數據
        {
            try
            {
                Convert.ToDouble(operand);
            }
            catch (Exception)
            {
                _result = "Invalid number: " + operand;
                throw;
            }
        }

        private void ValidateOperation(string operation)        //校驗運算符
        {
            switch (operation)
            {
                case "/":
                case "*":
                case "-":
                case "+":
                case "tan":
                case "cos":
                case "sin":
                    break;
                default:
                    _result = "Unknown operation: " + operation;
                    throw new ArgumentException("Unknown Operation: " + operation, "operation");
            }
        }

        private void ValidateData()     //校驗所有數據
        {
            switch (Operation)
            {
                case "/":
                case "*":
                case "-":
                case "+":
                    ValidateOperand(FirstOperand);
                    ValidateOperand(SecondOperand);
                    break;
                case "tan":
                case "cos":
                case "sin":
                    ValidateOperand(FirstOperand);
                    break;
                default:
                    _result = "Unknown operation: " + Operation;
                    throw new ArgumentException("Unknown Operation: " + Operation, "operation");
            }
        }

        #endregion
    }
}

ViewModels:CalculatorViewModel.cs

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using MvvmCalculator.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace MvvmCalculator.ViewModels
{
    public class CalculatorViewModel:ViewModelBase
    {
        #region 私有成員

        private CalculationModel calculation;

        private string lastOperation;   
        private bool newDisplayRequired = false;
        private string fullExpression;
        private string display;

        #endregion

        #region 構造函數

        public CalculatorViewModel()
        {
            this.calculation = new CalculationModel();
            this.display = "0";
            this.FirstOperand = string.Empty;
            this.SecondOperand = string.Empty;
            this.Operation = string.Empty;
            this.lastOperation = string.Empty;
            this.fullExpression = string.Empty;
        }

        #endregion

        #region 公共屬性

        public string FirstOperand      //第一個數
        {
            get { return calculation.FirstOperand; }
            set { calculation.FirstOperand = value; }
        }

        public string SecondOperand     //第二個數
        {
            get { return calculation.SecondOperand; }
            set { calculation.SecondOperand = value; }
        }

        public string Operation     //運算符
        {
            get { return calculation.Operation; }
            set { calculation.Operation = value; }
        }

        public string LastOperation     //最後運算符
        {
            get { return lastOperation; }
            set { lastOperation = value; }
        }

        public string Result        //結果
        {
            get { return calculation.Result; }
        }

        public string Display       //顯示數據
        {
            get { return display; }
            set
            {
                display = value;
                RaisePropertyChanged("Display");
            }
        }

        public string FullExpression    //表達式
        {
            get { return fullExpression; }
            set
            {
                fullExpression = value;
                RaisePropertyChanged("FullExpression");
            }
        }

        #endregion

        #region 命令

        public ICommand OperationButtonPressCommand     //操作符按鈕命令(=)
        {
            get
            {
                return new RelayCommand<string>((operation) =>
                {
                    try
                    {
                        if (FirstOperand == string.Empty || LastOperation == "=")   //第一個操作數爲空,則賦值爲0,或者計算完,拿結果作爲第一個操作數
                        {
                            FirstOperand = display;
                            LastOperation = operation;
                        }
                        else
                        {
                            SecondOperand = display;
                            Operation = lastOperation;
                            calculation.CalculateResult();

                            FullExpression = Math.Round(Convert.ToDouble(FirstOperand), 10) + " " + Operation + " "
                                            + Math.Round(Convert.ToDouble(SecondOperand), 10) + " = "
                                            + Math.Round(Convert.ToDouble(Result), 10);

                            LastOperation = operation;
                            Display = Result;
                            FirstOperand = display;     //將計算結果賦值給第一個操作數
                        }
                        newDisplayRequired = true;
                    }
                    catch (Exception)
                    {
                        Display = Result == string.Empty ? "Error " : Result;
                    }
                });
            }
        }

        public ICommand SingularOperationButtonPressCommand    //單操作數按鈕命令
        {
            get
            {
                return new RelayCommand<string>((operation) =>
                {
                    try
                    {
                        FirstOperand = Display;
                        Operation = operation;
                        calculation.CalculateResult();

                        FullExpression = Operation + "(" + Math.Round(Convert.ToDouble(FirstOperand), 10) + ") = "
                            + Math.Round(Convert.ToDouble(Result), 10);

                        LastOperation = "=";
                        Display = Result;
                        FirstOperand = display;
                        newDisplayRequired = true;
                    }
                    catch (Exception)
                    {
                        Display = Result == string.Empty ? "Error " : Result;
                    }
                });
            }
        }

        public ICommand DigitButtonPressCommand     //數字按鈕命令
        {
            get
            {
                return new RelayCommand<string>((button) =>
                {
                    switch (button)
                    {
                        case "C":       //清除按鈕
                            Display = "0";
                            FirstOperand = string.Empty;
                            SecondOperand = string.Empty;
                            Operation = string.Empty;
                            LastOperation = string.Empty;
                            FullExpression = string.Empty;
                            break;
                        case "Del":     //刪除按鈕
                            if (display.Length > 1)
                                Display = display.Substring(0, display.Length - 1);
                            else Display = "0";
                            break;
                        case "+/-":     //正數,負數
                            if (display.Contains("-") || display == "0")
                            {
                                Display = display.Remove(display.IndexOf("-"), 1);
                            }
                            else Display = "-" + display;
                            break;
                        case ".":
                            if (newDisplayRequired)     //如果需要重新顯示
                            {
                                Display = "0.";
                            }
                            else
                            {
                                if (!display.Contains("."))
                                {
                                    Display = display + ".";
                                }
                            }
                            break;
                        default:
                            if (display == "0" || newDisplayRequired)
                                Display = button;
                            else
                                Display = display + button;
                            break;
                    }
                    newDisplayRequired = false;
                });
            }
        }
        #endregion
    }
}

Views:Calculator.xaml

<Window x:Class="MvvmCalculator.Views.Calculator"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MvvmCalculator.ViewModels"
    Title="WPF Calculator"
    Height="340"
    Width="500" 
    ResizeMode="CanMinimize" >

    <DockPanel Name="MyPanel">
        <Grid Name="MyGrid" Background="Black"  ShowGridLines="False" Height="287" Width="475">
            <Grid.Resources>
                <Storyboard x:Key="playStoryboard">
                    <DoubleAnimation From="30" To="20" Duration="0:0:0.25" RepeatBehavior="1x" AutoReverse="True" Storyboard.TargetName="TB" Storyboard.TargetProperty="(Rectangle.Height)"/>
                    <DoubleAnimation From="60" To="40" Duration="0:0:0.25" RepeatBehavior="1x" AutoReverse="True" Storyboard.TargetName="TB" Storyboard.TargetProperty="(Rectangle.Width)"/>
                </Storyboard>
                <Style x:Key="DigitBtn"  TargetType="{x:Type Button}">
                    <Setter Property="Focusable" Value="False"/>
                    <Setter Property="FontSize" Value="14pt"/>
                    <Setter Property="Margin" Value="0"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type Button}">
                                <Grid Width="60" Height="32">
                                    <Rectangle RadiusX="10" RadiusY="10" Width="57" Height="30" x:Name="TB"  StrokeThickness="1"
                                    Stroke="{TemplateBinding Foreground}" Fill="{TemplateBinding Background}"
                                    HorizontalAlignment="Center" VerticalAlignment="Center" />
                                    <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                </Grid>

                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsMouseOver" Value="true">
                                        <Setter TargetName="TB" Property="Rectangle.Fill" Value="Gray" />
                                    </Trigger>
                                    <Trigger Property="IsPressed" Value="true">
                                        <Setter TargetName="TB" Property="Rectangle.Fill" Value="DarkSlateGray" />
                                    </Trigger>

                                    <EventTrigger RoutedEvent="ButtonBase.Click">
                                        <EventTrigger.Actions>
                                            <BeginStoryboard Name="playStoryboard" Storyboard="{StaticResource playStoryboard}"/>
                                        </EventTrigger.Actions>
                                    </EventTrigger>

                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>

            </Grid.Resources>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>

            <TextBlock  Name="DisplayBox" Text="{Binding Path=Display, Mode=TwoWay}" Background="White" Margin="20,10,20,2" Grid.ColumnSpan="5" FontSize="24" TextAlignment="Right"></TextBlock>
            <TextBlock  Name="ExpressionBox" Text="{Binding Path=FullExpression, Mode=TwoWay}" Background="DarkGray" TextAlignment="Center" Grid.Column="0" Grid.Row="1" Margin="20,10,20,10" Grid.ColumnSpan="3" FontSize="18"></TextBlock>

            <Button Name="Button7" Command="{Binding DigitButtonPressCommand}" CommandParameter="7" Style="{StaticResource DigitBtn}" Grid.Column="0" Grid.Row="2">7</Button>
            <Button Name="Button8" Command="{Binding DigitButtonPressCommand}" CommandParameter="8" Style="{StaticResource DigitBtn}" Grid.Column="1" Grid.Row="2">8</Button>
            <Button Name="Button9" Command="{Binding DigitButtonPressCommand}" CommandParameter="9" Style="{StaticResource DigitBtn}" Grid.Column="2" Grid.Row="2">9</Button>

            <Button Name="Button4" Command="{Binding DigitButtonPressCommand}" CommandParameter="4" Style="{StaticResource DigitBtn}" Grid.Column="0" Grid.Row="3" >4</Button>
            <Button Name="Button5" Command="{Binding DigitButtonPressCommand}" CommandParameter="5" Style="{StaticResource DigitBtn}" Grid.Column="1" Grid.Row="3" >5</Button>
            <Button Name="Button6" Command="{Binding DigitButtonPressCommand}" CommandParameter="6" Style="{StaticResource DigitBtn}" Grid.Column="2" Grid.Row="3" >6</Button>

            <Button Name="Button1" Command="{Binding DigitButtonPressCommand}" CommandParameter="1" Style="{StaticResource DigitBtn}" Grid.Column="0" Grid.Row="4" >1</Button>
            <Button Name="Button2" Command="{Binding DigitButtonPressCommand}" CommandParameter="2" Style="{StaticResource DigitBtn}" Grid.Column="1" Grid.Row="4" >2</Button>
            <Button Name="Button3" Command="{Binding DigitButtonPressCommand}" CommandParameter="3" Style="{StaticResource DigitBtn}" Grid.Column="2" Grid.Row="4" >3</Button>

            <Button Name="Button0" Command="{Binding DigitButtonPressCommand}" CommandParameter="0" Style="{StaticResource DigitBtn}" Grid.Column="0" Grid.Row="7" >0</Button>
            <Button Name="ButtonPoint" Command="{Binding DigitButtonPressCommand}" CommandParameter="." Style="{StaticResource DigitBtn}" Grid.Column="1" Grid.Row="7" >.</Button>

            <Button Name="ButtonPlusMinus" Command="{Binding DigitButtonPressCommand}" CommandParameter="+/-" Background="DarkGray" Style="{StaticResource DigitBtn}"  Grid.Column="2" Grid.Row="5" >+/-</Button>

            <Button Name="ButtonDivide" Command="{Binding OperationButtonPressCommand}" CommandParameter="/" Background="Darkgray" Style="{StaticResource DigitBtn}" Grid.Column="3" Grid.Row="2" >/</Button>
            <Button Name="ButtonMultiply" Command="{Binding OperationButtonPressCommand}" CommandParameter="*" Background="Darkgray" Style="{StaticResource DigitBtn}"  Grid.Column="3" Grid.Row="3" >*</Button>
            <Button Name="ButtonMinus" Command="{Binding OperationButtonPressCommand}" CommandParameter="-" Background="Darkgray" Style="{StaticResource DigitBtn}"  Grid.Column="3" Grid.Row="4" >-</Button>
            <Button Name="ButtonPlus" Command="{Binding OperationButtonPressCommand}" CommandParameter="+" Background="Darkgray" Style="{StaticResource DigitBtn}"  Grid.Column="3" Grid.Row="5" >+</Button>

            <Button Name="ButtonC"  Command="{Binding DigitButtonPressCommand}" CommandParameter="C" Background="Darkgray" Style="{StaticResource DigitBtn}"  Grid.Column="4" Grid.Row="1" ToolTip="Clear All">C</Button>
            <Button Name="ButtonDel" Command="{Binding DigitButtonPressCommand}" CommandParameter="Del" Background="Darkgray" Style="{StaticResource DigitBtn}"  Grid.Column="3" Grid.Row="1" ToolTip="Delete">Del</Button>

            <Button Name="ButtonSin" Command="{Binding SingularOperationButtonPressCommand}" CommandParameter="sin" Background="Darkgray"  Style="{StaticResource DigitBtn}" Grid.Column="4" Grid.Row="2">Sin(x)</Button>
            <Button Name="ButtonCos" Command="{Binding SingularOperationButtonPressCommand}" CommandParameter="cos" Background="Darkgray" Style="{StaticResource DigitBtn}" Grid.Column="4" Grid.Row="3">Cos(x)</Button>
            <Button Name="ButtonTan" Command="{Binding SingularOperationButtonPressCommand}" CommandParameter="tan" Background="Darkgray" Style="{StaticResource DigitBtn}" Grid.Column="4" Grid.Row="4">Tan(x)</Button>

            <Button Name="ButtonEqual" Command="{Binding OperationButtonPressCommand}" CommandParameter="=" Background="Darkgray" Style="{StaticResource DigitBtn}"  Grid.Column="4" Grid.Row="5" Margin="0,0,0,0">=</Button>

        </Grid>
    </DockPanel>

</Window>

App.xaml.cs:

namespace MvvmCalculator
{
    /// <summary>
    /// App.xaml 的交互邏輯
    /// </summary>
    public partial class App : Application
    {
        private void OnStartup(object sender, StartupEventArgs e)
        {
            // 建立ViewModel和使用ViewModel作爲DataContext
            Views.Calculator view = new Views.Calculator();
            view.DataContext = new ViewModels.CalculatorViewModel();
            view.Show();
        }
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章