I am posting two examples of Calculator code that work. They both have different but effective approaches. The first is From Victoria Bailey. I have only included the code from the form.
Victoria's only works with binary operations, that is [number] [operator] [number], for example 4 + 2, 3 * 5, etc. It doesn't work with examples like 4 + 3 * 2 / 2. Christian's does
Default.aspx
<form id="form1" runat="server">
<div>
<asp:Label ID="lblScreen" runat="server" Text=""></asp:Label><br />
<asp:Button ID="Button1" runat="server" Text="1"
onclick="btnNumber_Click" /><asp:Button ID="Button2"
runat="server" Text="2" onclick="btnNumber_Click" />
<asp:Button ID="Button3" runat="server" Text="3" onclick="btnNumber_Click" /><br />
<asp:Button ID="Button4" runat="server"
Text="4" onclick="btnNumber_Click" /><asp:Button ID="Button5"
runat="server" Text="5" onclick="btnNumber_Click" />
<asp:Button ID="Button6" runat="server" Text="6" onclick="btnNumber_Click" /><br />
<asp:Button ID="Button7" runat="server"
Text="7" onclick="btnNumber_Click" /><asp:Button ID="Button8"
runat="server" Text="8"
onclick="btnNumber_Click" />
<asp:Button ID="Button9" runat="server" Text="9"
onclick="btnNumber_Click" /><br />
<asp:Button ID="Button10" runat="server" Text="0"
onclick="btnNumber_Click" /><br />
<asp:Button ID="Button11" runat="server" Text="+"
onclick="btnOperation_Click" />
<asp:Button ID="Button12" runat="server" Text="-"
onclick="btnOperation_Click" />
<asp:Button ID="Button13" runat="server" Text="*"
onclick="btnOperation_Click" />
<asp:Button ID="Button14" runat="server" Text="/"
onclick="btnOperation_Click" />
<br /><br />
<asp:Button ID="btnCalculate"
runat="server" Text="Calculate" onclick="btnCalculate_Click" /><br /><br />
<asp:Label ID="lblAnswer" runat="server" Text="Answer: "></asp:Label>
<asp:Label ID="lblCalculation" runat="server" Text=""></asp:Label><br /><br />
</div>
</form>
Code Behind:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnCalculate_Click(object sender, EventArgs e)
{
string op = Session["operation"].ToString();
double number = 0;
double secondNumber = 0;
double total = 0;
if (lblScreen.Text != String.Empty)
{
secondNumber = double.Parse(lblScreen.Text);
}
if ((Session["firstNumber"] != null) && (Session["firstNumber"].ToString() != ""))
{
number = double.Parse(Session["firstNumber"].ToString());
}
switch (op)
{
case "+":
total = number + secondNumber;
break;
case "-":
total = number - secondNumber;
break;
case "*":
total = number * secondNumber;
break;
case "/":
total = number / secondNumber;
break;
default:
break;
}
lblCalculation.Text = total.ToString();
}
protected void btnNumber_Click(object sender, EventArgs e)
{
Button button = (Button)sender;
lblScreen.Text += button.Text;
}
protected void btnOperation_Click(object sender, EventArgs e)
{
Session["firstNumber"] = lblScreen.Text;
Button button = (Button)sender;
Session["operation"] = button.Text;
lblScreen.Text = String.Empty;
}
The second example is from Christian Roehr. It uses a very different approach. It is also well commented. Again I have only included the source from the form
>
<form id="form1" runat="server">
<div>
<table>
<tbody>
<tr>
<td colspan="4"></td>
<asp:TextBox ID="txtDisplay" runat="server"></asp:TextBox>
</tr>
<tr>
<td>
<asp:Button ID="Button1" runat="server" Text="7"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button2" runat="server" Text="8"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button3" runat="server" Text="9"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="btnAdd" runat="server" Text="+"
CssClass="buttonstyle" OnClick="Operator_Click"/></td>
</tr>
<tr>
<td>
<asp:Button ID="Button5" runat="server" Text="4"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button6" runat="server" Text="5"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button7" runat="server" Text="6"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="btnSubtract" runat="server" Text="-"
CssClass="buttonstyle" OnClick="Operator_Click"/></td>
</tr>
<tr>
<td>
<asp:Button ID="Button9" runat="server" Text="1"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button10" runat="server" Text="2"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button11" runat="server" Text="3"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="btnMult" runat="server" Text="*"
CssClass="buttonstyle" OnClick="Operator_Click"/></td>
</tr>
<tr>
<td>
<asp:Button ID="btnDel" runat="server" Text="Del" ToolTip="Use the <Del> button to clear the textbox in order to correct a value before clicking one of the operator buttons. The intermediate result and last operator are still kept in memory." CssClass="buttonstyle"
onclick="btnDel_Click"/></td>
<td>
<asp:Button ID="Button14" runat="server" Text="0"
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="Button15" runat="server" Text="."
CssClass="buttonstyle" OnClick="InputNumber_Click"/></td>
<td>
<asp:Button ID="btnDiv" runat="server" Text="/"
CssClass="buttonstyle" OnClick="Operator_Click"/></td>
</tr>
<tr>
<td>
<asp:Button ID="btnClear" runat="server" Text="CL" ToolTip="Use the <CL> button to reset the calculator to its original state. Any prior values or operators are lost." CssClass="buttonstyle" OnClick="btnCl_Click"/></td>
<td colspan="3">
<asp:Button ID="btnEqual" runat="server" Text="=" CssClass="buttonstyle" OnClick="Operator_Click"/></td>
</tr>
</tbody>
</table>
</div>
</form>
</body>
</html>
======================== default.aspx.cs
using System;
using System.Diagnostics; // needed for "Debug" class
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
// define unique integers to identify one of
// {+,-,*,/,=}
protected enum OperatorTypes
{
UNKNOWN=0,
ADD = 1,
SUBTRACT = 2,
MULT = 3,
DIVIDE = 4,
EQUALS = 5
};
protected const double m_initialValue = 0.0;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) // wonna do it when page loads but not every time when we push a button
{
InitializeCalculator();
}
}
protected void InitializeCalculator()
{
// START: define the session variables
//
// Here is why the operator is initially "+".
// When the user clicks any operator
// op_new = one of {+,-,*,/,=}
// then the evaluation of the
// _previous_ operator (op_prev ) happens and _not_ the one that was just clicked (op_new).
// Thus, if we want everything to work the same and if we want to save ourselves
// too much extra specialized initialization-specific code, we can just assume that
// the very first op_prev was "+" and that the previous result (result_prev) was 0.0.
// Then , when op_new is clicked, we evaluate
// result_new = execute(result_prev, op_prev, number);
// Where "number" is the number the user typed in prior to pressing op_new. "number"
// got parsed right when we clicked op_new.
// We could just as well have selected Session["result"] = 1.0 and Session["operator"] = "*"
// but "+" is simpler and somehow cleaner, avoiding possible rounding errors.
//
// In any future discussion, i will call the session variables
// the "stack", since that is what we kind of do when we
// store the value on the server side and update it or retrieve it.
Session["result"] = m_initialValue; // this writes it to the server and gives it a value of 0.0
Session["operator"] = OperatorTypes.ADD;
// END: define the session variables
// in order to facilitate the very first parsing of a number
// down the line, let's put a "0" into the text field.
// That is how most calculators work and it also
// puts it in sync with what is in our initial session "result" value.
InitializeTextDisplay();
}
protected void InitializeTextDisplay()
{
txtDisplay.Text = m_initialValue.ToString();
}
protected void EmptyTextDisplayIfContainsInitialValue()
{
if (txtDisplay.Text == m_initialValue.ToString())
{
txtDisplay.Text = "";
}
}
protected void InputNumber_Click(object sender, EventArgs e)
{
// avoid this scenario from happening:
// after entering a number, get something like 06, 0555, 000, 0024
// We dont want such numbers since they may not be parsable, or even if
// they are, they dont look pretty.
EmptyTextDisplayIfContainsInitialValue();
// the button that is sending the message
Button b = (Button)sender;
txtDisplay.Text += b.Text;
}
protected void Operator_Click(object sender, EventArgs e)
{
// the button that is sending the message
Button b = (Button)sender;
// we could use the ID or we could use the text
// START: determine which operator was clicked
//
// FYI: Equals() operator more optimized to work with strings, but could have done '==' too
//
OperatorTypes op_new = OperatorTypes.UNKNOWN;
if (b.ID.Equals("btnAdd"))
{
op_new = OperatorTypes.ADD;
}
else if (b.ID.Equals("btnSubtract"))
{
op_new = OperatorTypes.SUBTRACT;
}
else if (b.ID.Equals("btnMult"))
{
op_new = OperatorTypes.MULT;
}
else if (b.ID.Equals("btnDiv"))
{
op_new = OperatorTypes.DIVIDE;
}
else if (b.ID.Equals("btnEqual"))
{
op_new = OperatorTypes.EQUALS;
}
else
{
// this should not happen anyways if we wrote
// the code correctly.
op_new = OperatorTypes.UNKNOWN;
}
// for debugging only
Debug.Write(String.Format("op_new={0}\n", (int)op_new));
if (op_new == OperatorTypes.UNKNOWN)
{
Exception ex = new Exception("Invalid operator");
throw ex;
}
// END: determine which operator was clicked
// 1. Parse the number which user had entered up to
// the point of clicking one of the operator buttons.
double operandNumber = GetNumber();
// 2. Get the last result from the stack.
double result_prev = (double)Session["result"];
// 3. Get the operator from the stack.
// This operator is _not_ the one we just got, but the one
// entered before user entered <operandNumber>
OperatorTypes op_prev = (OperatorTypes)Session["operator"];
Debug.WriteLine("op_prev={0}\n", (int)op_prev);
// 4. evaluate the new result based
// on <result_prev>, <op_prev>, <operandNumber>
// For instance, if op_prev is "*" (multiplication), then
//
// result_new = result_prev * operandNumber
double result_new = ExecuteOperator(result_prev, op_prev, operandNumber);
Debug.WriteLine("result_new={0}\n", result_new);
// test if the number is wellformed. That can be for one of the following reasons
// a) divide-by-zero
// b) overflow (two very large numbers multiplied or a very small one in the denominator)
// c) perhaps some other reason
if (double.IsInfinity(result_new) || double.IsNaN(result_new))
{
// first reset the calculator since you cannot utilize the result for any future operation
InitializeCalculator();
// throw an exception
string numberString = string.Format("resulting number = {0} .", result_new);
Exception ex = new Exception(numberString+ "You either divided by zero or the operands resulted in an out-of-range number");
throw ex;
}
// 5. put new result on the stack
Session["result"] = result_new;
// 6. put new operator on the stack
Session["operator"] = op_new;
// 7. display the new result
if (op_new == OperatorTypes.EQUALS)
{
// return result to text box
txtDisplay.Text = result_new.ToString();
}
else
{
// as shown in the programming assignment specification,
// ("and clear it to make it ready for the next entry")
// clear the text (even though that is not the typical
// calculator behavior i think. the typical calculator
// shows the intermittent result and only clears after
// you press the first digit of
// the new number you are about to enter)
InitializeTextDisplay();
}
}
protected double GetNumber()
{
// double number = double.Parse(txtDisplay.Text);
double number = 0;
bool isDouble = double.TryParse(txtDisplay.Text, out number);
if (isDouble == false)
{
txtDisplay.Text = "";
Exception ex = new Exception("Only enter valid numbers");
throw ex;
}
return number;
}
// Perform the operation
// returnvalue = result_old op num
// e.g. if op=OperatorTypes.ADD
// you will get
// returnvalue = result_old + num
protected double ExecuteOperator(
double result_old,
OperatorTypes op,
double num
)
{
double result = result_old; // default in case of an invalid operator
switch (op)
{
case OperatorTypes.ADD:
result = result_old + num;
break;
case OperatorTypes.SUBTRACT:
result = result_old - num;
break;
case OperatorTypes.MULT:
result = result_old * num;
break;
case OperatorTypes.DIVIDE:
result = result_old / num;
break;
case OperatorTypes.EQUALS:
result = result_old;
break;
default:
result = result_old;
break;
}
return result;
}
protected void btnDel_Click(object sender, EventArgs e)
{
// as specified in the assignment
// have a "clear" button to clear the textbox.
InitializeTextDisplay();
}
protected void btnCl_Click(object sender, EventArgs e)
{
// if we dont have a CLEAR button to reset everything,
// I found that funky things happen where the operators
// stop doing the right things after clicking a certain
// combination of equal sign and operators etc.
// Thus we must have a way to clear all memory and reset
// the stack to the beginning of what it was when the calculator
// first started.
InitializeCalculator();
}
}
No comments:
Post a Comment