using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace PS7_Q2 { // // Problem Set 7, Question 2 // Written by Paul Hudak // November 5, 2006 // // A Simple Calculator // ------------------- /* The specification: * The width and height of the window form should be 225 and 320, respectively. * The text for the top-level form is "Calculator". There is a label component (size 175, 35) * at the top (with white background color, text aligned to the right, size 16 regular font). * There are 15 buttons (each size 48x23, size 14 bold font): ten of these are for digits; * three are for the addition, subtraction, and multiplication buttons; the "C" button clears * the text in the label to zero; and the "=" button prints out the result. */ // create two new button types by inheriting from Button // numeric button (0-9) public class NumButton : Button { // the value associated with a numeric button public int Value; } // operator button (+,-,*) public class OpButton : Button { // the binary operator (delegate) associated with an operator button public BinOp Op; } // delegate to capture the binary operator public delegate int BinOp(int i, int j); // the main calculator Form public class Form1 : System.Windows.Forms.Form { // create widgets // array of numeric buttons private NumButton[] buts = {new NumButton(), new NumButton(), new NumButton(), new NumButton(), new NumButton(), new NumButton(), new NumButton(), new NumButton(), new NumButton(), new NumButton() }; private Label display = new Label(); // display area private OpButton add = new OpButton(); // add button private OpButton sub = new OpButton(); // substract button private OpButton mul = new OpButton(); // multiply button private Button clear = new Button(); // clear button private Button enter = new Button(); // enter button // instance variables for calculator state private int num = 0; // the current number in display private int hidden = 0; // the hidden number private bool flag = false; // hidden number not valid private BinOp selectD = new BinOp(SelectD); // a binop that selects the Display private BinOp binOp; // the current binary operator private int lastBut = 0; // last button (0=Clear, 1=Number, // 2=Op, 3=Enter) private System.ComponentModel.Container components = null; public Form1() { // Required for Windows Form Designer support InitializeComponent(); // set form properties Size = new Size(225,320); Text = "Calculator"; // create two fonts Font f1 = new Font("Comic Sans MS", 16, FontStyle.Bold); Font f2 = new Font("Comic Sans MS", 14, FontStyle.Bold); // set all properties for each of the 10 numeric buttons for (int i=0; i<10; i=i+1) { buts[i].Font = f2; buts[i].Text = i.ToString(); buts[i].Value = i; buts[i].Location = new Point(20+(i%3)*68,75+(i/3)*43); buts[i].Size = new Size(48,23); buts[i].Click += new EventHandler(NumberClick); Controls.Add(buts[i]); } // set display properties display.TextAlign = ContentAlignment.MiddleRight; // System.Drawing.ContentAlignment display.BorderStyle = BorderStyle.Fixed3D; display.BackColor = Color.White; // set fonts display.Font = f1; add.Font = f2; sub.Font = f2; mul.Font = f2; clear.Font = f2; enter.Font = f2; // set text properties display.Text = "0"; add.Text = "+"; sub.Text = "-"; mul.Text = "*"; clear.Text = "C"; enter.Text = "="; // set size properties display.Size = new Size(175,35); add.Size = new Size(48,23); sub.Size = new Size(48,23); mul.Size = new Size(48,23); clear.Size = new Size(48,23); enter.Size = new Size(48,23); // set location properties display.Location = new Point(20,20); add.Location = new Point( 88,75+3*43); sub.Location = new Point(156,75+3*43); mul.Location = new Point( 20,75+4*43); clear.Location = new Point( 88,75+4*43); enter.Location = new Point(156,75+4*43); // set Op property in each operator button to appropriate delegate add.Op = new BinOp(AddOp); sub.Op = new BinOp(SubOp); mul.Op = new BinOp(MulOp); // set default binOp binOp = selectD; // add controls to form Controls.Add(display); Controls.Add(add); Controls.Add(sub); Controls.Add(mul); Controls.Add(clear); Controls.Add(enter); // register event handlers enter.Click += new EventHandler(EnterClick); clear.Click += new EventHandler(ClearClick); add.Click += new EventHandler(OpClick); sub.Click += new EventHandler(OpClick); mul.Click += new EventHandler(OpClick); } /* The logic for the operation of the calculator is more complex than it first appears. * We need to account for all possible transitions of key presses. I believe that the * following captures the behavior of the Windows XP Calculator. * * Let D be the display number, H the hidden number, Op the current operation, and * F the boolean flag saying whether or not the hidden number is valid. * Initial state: D=0, H=0, F=false, Op=selectD, where selectD(D,H) = D * * Clear behaves the same regardless of when it is pressed -- it returns the * calculator to its initial state -- and thus it's not shown below. For the other * three categories of keys, we consider the previous key in each case, and also * whether or not a hidden number is active. /* * | Previous key press: * Key-press | Clear(0) Number(1) Op(2) Enter(3) * --------------------------------------------------------------------------------------------- * Number | upd D upd D D=0, upd D H=0, D=0, F=false, upd D * Op | upd Op F: upd Op, Swap, upd Op H=D, upd Op * | D=Op(D,H) * | !F: upd Op, H=D, * | F=true * Enter | noop F: Swap,D=Op(D,H) H=D, D=Op(D,H) D=Op(D,H) * | !F: H=D, F=true */ protected void ClearClick(Object sender, EventArgs e) { // reset all instance variables and clear display num = 0; hidden = 0; binOp = selectD; flag = false; display.Text = num.ToString(); lastBut = 0; } protected void NumberClick(Object sender, EventArgs e) { // follows logic in chart above switch (lastBut) { case 2: num = 0; break; case 3: hidden = 0; num = 0; flag = false; break; } // update num by multiplying by 10 and adding current key press num = 10*num + ((NumButton)sender).Value; display.Text = num.ToString(); lastBut = 1; } protected void OpClick(Object sender, EventArgs e) { // follows logic in chart above binOp = ((OpButton)sender).Op; switch (lastBut) { case 1: if (flag) { Swap(); Compute(); } else { hidden = num; flag = true; } break; case 3: hidden = num; break; } lastBut = 2; } protected void EnterClick(Object sender, EventArgs e) { // follows logic in chart above switch (lastBut) { case 0: // last button was Clear break; case 1: // last button was Number if (flag) { Swap(); Compute(); } else { hidden = num; flag = true; } break; case 2: // last button was Op hidden = num; // duplicate num Compute(); break; case 3: // last button was Enter Compute(); break; } lastBut = 3; } private void Swap() { // swaps display num with hidden num int tmp = hidden; hidden = num; num = tmp; } private void Compute() { // use delegate to do computation num = binOp(num,hidden); // update display display.Text = num.ToString(); } // define these because I don't know how to // reference C#'s binary operators as values private static int AddOp(int i, int j) { return (i+j); } private static int SubOp(int i, int j) { return (i-j); } private static int MulOp(int i, int j) { return (i*j); } private static int SelectD(int d, int h) { return (h); } // Clean up any resources being used. protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.Size = new System.Drawing.Size(300,300); this.Text = "Form1"; } #endregion // The main entry point for the application. [STAThread] static void Main() { Application.Run(new Form1()); } } }