通り*さん投稿の電卓アプリです。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace ToyCalc
{
/// <SUMMARY>
/// 画面
/// </SUMMARY>
public partial class Form1 : Form
{
private ToyCalc _Calc;
public Form1()
{
InitializeComponent();
// 電卓表示
InitToyCalcForm();
// 計算結果はフォームのタイトルへ表示する
// (バインド方法は、とりこびとさんのエントリで勉強させてもらいました)
_Calc = new ToyCalc();
this.DataBindings.Add("Text", _Calc, "ValueForDisplay", false, DataSourceUpdateMode.OnPropertyChanged);
}
///
/// 電卓表示
///
private void InitToyCalcForm()
{
this.Size = new Size(200, 200);
// ボタンをレイアウトするためのパネルの準備
TableLayoutPanel tlp = new TableLayoutPanel();
tlp.ColumnCount = 4;
for (int i = 0; i < tlp.ColumnCount; i++)
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / tlp.ColumnCount));
tlp.RowCount = 5;
for (int i = 0; i < tlp.RowCount; i++)
tlp.RowStyles.Add(new RowStyle(SizeType.Percent, 100f / tlp.RowCount));
tlp.AutoSize = true;
tlp.Dock = DockStyle.Fill;
this.Controls.Add(tlp);
// 表示するボタンの順番
ToyCalcButtons[] buttons =
{
ToyCalcButtons.Clear, ToyCalcButtons.ClearEntry, ToyCalcButtons.Divide, ToyCalcButtons.Multiply,
ToyCalcButtons.Seven, ToyCalcButtons.Eight, ToyCalcButtons.Nine, ToyCalcButtons.Subtract,
ToyCalcButtons.Four, ToyCalcButtons.Five, ToyCalcButtons.Six, ToyCalcButtons.Add,
ToyCalcButtons.One, ToyCalcButtons.Two, ToyCalcButtons.Three, ToyCalcButtons.Equal,
ToyCalcButtons.Zero
};
string[] buttonCaptions =
{
"C", "CE", "÷", "×",
"7", "8", "9", "-",
"4", "5", "6", "+",
"1", "2", "3", "=",
"0"
};
// ボタンを生成
Button b;
for (int i = 0; i < buttons.Length; i++)
{
b = new Button();
b.Dock = DockStyle.Fill;
b.Text = buttonCaptions[i];
b.Tag = buttons[i];
// (匿名メソッドの存在をすっかり忘れていましたが、エピさんのソースを拝見して思い出しました)
b.Click += delegate(object sender, eventArgs e) { _Calc.Push((ToyCalcButtons)((Button)sender).Tag); };
tlp.Controls.Add(b);
}
// コピーボタンが押されたら、表示内容をクリップボードへ貼り付ける
b = new Button();
b.Dock = DockStyle.Fill;
b.Text = "コピー";
b.Click += delegate { Clipboard.SetText(this.Text); };
tlp.Controls.Add(b);
}
}
///
/// 各ボタンを表す列挙型
///
public enum ToyCalcButtons
{
// Zero~Nineは数値にキャストして使います
Zero = 0, One, Two, Three, Four, Five, Six, Seven, Eight, Nine,
Clear, ClearEntry,
Add, Subtract, Multiply, Divide,
Equal
}
///
/// おもちゃ計算機
///
public class ToyCalc : INotifyPropertyChanged
{
// 変数達(絶対どれかいらなさそう...)
private decimal _Result;
private decimal _CurrValue;
private decimal _LastValue;
private ToyCalc _Stack;
private ToyCalcButtons _LastPushed;
private ToyCalcButtons _PrevPushed;
private ToyCalcButtons _PrevOperator; // Equal は設定されない
private bool _PrevOperatorIsEqual; // Equal はこちらで判定する
private bool _PrevOperatorIsValid;
private string _ErrorReason;
public ToyCalc()
{
// まずクリア!
Push(ToyCalcButtons.Clear);
}
///
/// ボタンが押されたときの処理
///
public void Push(ToyCalcButtons button)
{
// エラー状態の場合はクリアしかできません
if (_ErrorReason != "" && button != ToyCalcButtons.Clear)
return;
if (IsInStack(button)) return;
_LastPushed = button;
switch (getButtonTypeOf(button))
{
case ButtonType.Clear:
// クリア!
Clear(button == ToyCalcButtons.ClearEntry);
break;
case ButtonType.Num:
// 数字ボタンの処理
if (_PrevPushed == ToyCalcButtons.Equal)
_Result = 0;
BuildCurrValue(button);
break;
case ButtonType.Operator:
// 演算子ボタンの処理
Calculate(button);
break;
}
_PrevPushed = _LastPushed;
}
///
/// スタックでの計算に関する処理
///
/// <returns>スタックに計算を任せた場合、truereturns>
private bool IsInStack(ToyCalcButtons button)
{
if (_Stack == null && !_PrevOperatorIsEqual)
{
switch (button)
{
case ToyCalcButtons.Multiply:
case ToyCalcButtons.Divide:
if (_PrevOperatorIsValid)
{
// "+*" への対処。"+" でいったん計算が確定するので、スタックには入らないように
switch (_PrevPushed)
{
case ToyCalcButtons.Add:
case ToyCalcButtons.Subtract:
return false;
}
// "1+2*"の場合、"2"のところから別ので計算してもらう
switch (_PrevOperator)
{
case ToyCalcButtons.Add:
case ToyCalcButtons.Subtract:
// もう一つ生成
_Stack = new ToyCalc();
// "2*" の "*" でここへ来るので、"2" をセットしておく
if (getButtonTypeOf(_PrevPushed) == ButtonType.Num)
_Stack._CurrValue = this._CurrValue;
_Stack.PropertyChanged += new PropertyChangedeventHandler(_Stack_PropertyChanged);
break;
}
}
break;
}
}
if (_Stack != null)
{
switch (button)
{
case ToyCalcButtons.Clear:
_Stack = null;
break;
case ToyCalcButtons.Add:
case ToyCalcButtons.Subtract:
// "1+2*+" は "1+2*[2]+" と解釈しないで、"*" が "+" に訂正されたと見なします
if (getButtonTypeOf(_Stack._PrevPushed) != ButtonType.Num)
_Stack._LastPushed = ToyCalcButtons.Equal;
// 後は "=" と同じ処理
goto case ToyCalcButtons.Equal;
case ToyCalcButtons.Equal:
// スタックから計算結果をもらう
_CurrValue = _Stack.Result;
_Stack = null;
break;
}
}
if (_Stack != null)
{
// スタックに処理してもらう
_Stack.Push(button);
_ErrorReason = _Stack._ErrorReason;
return true;
}
return false;
}
///
/// クリア!
///
private void Clear(bool onlyCurrentEntry)
{
_CurrValue = 0m;
_LastValue = 0m;
if (!onlyCurrentEntry)
{
_Result = 0m;
_Stack = null;
// (最初の入力値は 0+N になるように初期値を Add にしておきます)
_PrevOperator = ToyCalcButtons.Add;
_PrevOperatorIsValid = false;
_PrevOperatorIsEqual = false;
_ErrorReason = "";
}
// バインド先の値が更新されますように...
NotifyPropertyChanged("ValueForDisplay");
}
///
/// 数字ボタンの処理
///
private void BuildCurrValue(ToyCalcButtons button)
{
try
{
checked // ←decimalだといらないみたい?
{
_CurrValue = _CurrValue * 10m + (decimal)button;
}
}
catch (OverflowException)
{
System.Media.SystemSounds.Beep.Play();
}
_LastValue = _CurrValue;
NotifyPropertyChanged("ValueForDisplay");
}
///
/// +や*などの処理
///
private void Calculate(ToyCalcButtons button)
{
bool through = false;
if (button == ToyCalcButtons.Equal)
{
if (_PrevOperatorIsValid)
{
// "1+2==" を "1+2=3[+2]=" として処理
if (_PrevOperatorIsEqual)
_CurrValue = _LastValue;
// "1+2*3*=" を "1+2*3*[2*3]=" として処理
else if (getButtonTypeOf(_PrevPushed) != ButtonType.Num)
_CurrValue = _Result;
}
}
else
{
// "*+" の場合、訂正とみなして前の演算子は無視
// (ただし "+*" の場合、"+" は無視しない)
through =
_PrevPushed == ToyCalcButtons.Multiply
|| _PrevPushed == ToyCalcButtons.Divide
|| _PrevPushed == ToyCalcButtons.Equal;
}
if (!through)
{
try
{
checked
{
// "1+2-" の "-" 時点で "1+2" を行うので、一つ前の演算子で計算する
switch (_PrevOperator)
{
case ToyCalcButtons.Add:
_Result += _CurrValue;
break;
case ToyCalcButtons.Subtract:
_Result -= _CurrValue;
break;
case ToyCalcButtons.Multiply:
_Result *= _CurrValue;
break;
case ToyCalcButtons.Divide:
_Result /= _CurrValue;
_Result = decimal.Floor(_Result);
break;
default:
throw new Exception("不明な演算子");
}
}
}
catch (DivideByZeroException)
{
_ErrorReason = "ゼロ除算エラー";
}
catch (OverflowException)
{
// アンダーフローもここ
_ErrorReason = "オーバーフロー";
}
_LastValue = _CurrValue;
_CurrValue = 0m;
NotifyPropertyChanged("ValueForDisplay");
}
if (button != ToyCalcButtons.Equal)
{
_PrevOperator = button;
_PrevOperatorIsValid = true;
_PrevOperatorIsEqual = false;
}
else
_PrevOperatorIsEqual = true;
}
///
/// 表示用の値
///
public string ValueForDisplay
{
get
{
if (_ErrorReason != "")
return _ErrorReason;
decimal value;
if (_Stack == null)
{
// "1+2" の時点では入力中の "2"、
// "1+2+" の時点では計算結果の "3" を返します
value =
(getButtonTypeOf(_LastPushed) == ButtonType.Num)
? _CurrValue : _Result;
}
else
{
// スタックでの計算中は、常に入力中の値
value = _Stack._LastValue;
}
return value.ToString();
}
}
///
/// 計算結果
///
public decimal Result
{
get
{
// 計算の途中であれば "=" を押して終わらせる
if (_LastPushed != ToyCalcButtons.Equal)
Push(ToyCalcButtons.Equal);
return _ErrorReason == "" ? _Result : 0m;
}
}
#region ボタンのタイプ
private enum ButtonType { Clear, Num, Operator }
[System.Diagnostics.DebuggerStepThrough]
private ButtonType getButtonTypeOf(ToyCalcButtons button)
{
switch (button)
{
case ToyCalcButtons.Clear:
case ToyCalcButtons.ClearEntry:
return ButtonType.Clear;
case ToyCalcButtons.Zero:
case ToyCalcButtons.One:
case ToyCalcButtons.Two:
case ToyCalcButtons.Three:
case ToyCalcButtons.Four:
case ToyCalcButtons.Five:
case ToyCalcButtons.Six:
case ToyCalcButtons.Seven:
case ToyCalcButtons.Eight:
case ToyCalcButtons.Nine:
return ButtonType.Num;
case ToyCalcButtons.Add:
case ToyCalcButtons.Subtract:
case ToyCalcButtons.Multiply:
case ToyCalcButtons.Divide:
case ToyCalcButtons.Equal:
return ButtonType.Operator;
default:
throw new ArgumentException("不明なボタン", "button");
}
}