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