Windows Forms and Visual Studio .NET

Windows Forms and Visual Studio .NET

Building applications by hand is a great way to learn the mechanics of Windows Forms, but in practice, most Windows Forms developers use Visual Studio .NET to craft their wares. Visual Studio .NET is the version of Visual Studio for .NET Framework developers. Its many features include an integrated editor, compiler, and debugger, context-sensitive help in the form of IntelliSense, and a built-in forms designer that simplifies the task of laying out and implementing Windows forms.

To acquaint you with Windows Forms development Visual Studio .NET style, the remaining sections of this chapter provide step-by-step instructions for building a very practical Windows Forms application a reverse Polish notation (RPN) calculator capable of performing addition, subtraction, multiplication, and division. Figure 4-23 shows the finished product.

Figure 4-23

The NetCalc application.

In case you re not familiar with RPN calculators, here s a brief tutorial. To add 2 and 2 on a conventional calculator, you press the following keys:

2 + 2 =

To add 2 and 2 on an RPN calculator, you do this instead:

2 Enter 2 +

The first two key presses 2 and Enter push a 2 onto the calculator s internal evaluation stack. Pressing 2 again pushes another 2 onto the stack and moves the other one up one slot. Pressing the plus key (+) pops both 2s off the stack, adds them together, and pushes the result onto the stack. A 4 appears in the calculator s output window because the value at the top of the stack is always shown there.

One of the beauties of RPN calculators is that you don t need parentheses, and even complex expressions can be evaluated with a minimum of effort. Suppose, for example, you were given the following expression:

((2 + 2) * 6) / 3

Evaluating this expression would be a chore on a conventional calculator (especially one that lacks parentheses), but not on an RPN calculator. Here s the sequence of keystrokes:

2 Enter 2 + 6 * 3 /

RPN was popularized on Hewlett-Packard scientific calculators in the 1970s. Once you get used to crunching numbers the RPN way, you ll never want to use a conventional calculator again. But enough about the virtues of RPN. Let s write some code.

Step 1: Create a Project

The first step in creating a Windows Forms application in Visual Studio .NET is to create a project. To begin, choose the New/Project command from Visual Studio .NET s File menu. When the New Project dialog appears, make sure Visual C# Projects is selected in the Project Types box, as shown in Figure 4-24. Then choose Windows Application to identify the kind of application you intend to create. Type NetCalc into the Name box to give the project a name, and then click the OK button.

Figure 4-24

Creating a C# Windows Forms project.

Step 2: Design the Form

After creating the project, Visual Studio .NET will drop you into its forms designer, which is a visual tool for adding controls to forms, setting properties on those controls, writing event handlers, and more. The forms designer initially displays a blank form.

The next order of business is to customize the title at the top of the form. The default is Form1. Change that by locating the form s Text property in the Properties window (by default, the Properties window appears in the lower right corner of Visual Studio .NET s main window) and changing Form1 to NetCalc. The new title should appear in the form s title bar in the forms designer. While you re at it, make the form nonresizable by setting its Form BorderStyle property to FixedDialog, and disable its maximize box by setting MaximizeBox to false. The following table summarizes the properties that need to be changed:

Property

Value

Text

NetCalc

FormBorderStyle

FixedDialog

MaximizeBox

False

On the left side of the Visual Studio .NET window is a vertical button labeled Toolbox. Moving the cursor over the button and pausing momentarily causes a palette to appear, listing the various controls that you can add to a Windows form. Create a form that resembles the one shown in Figure 4-25 by selecting Button and TextBox controls from the palette and positioning them on the form. Then use the Properties window to apply the following properties to the TextBox:

Property

Value

TextAlign

Right

TabStop

False

Text

0.00

ReadOnly

True

BackColor

Window

Cursor

Default

Name

Display

Font

10-point Microsoft Sans Serif

Next use the Properties window to assign the following values to the buttons Text and Name properties:

Text

Name

Text

Name

Enter

EnterButton

1

OneButton

Fix

FixButton

2

TwoButton

Clear

ClearButton

3

ThreeButton

Del

DeleteButton

4

FourButton

.

DecimalButton

5

FiveButton

-

SubtractButton

6

SixButton

+

AddButton

7

SevenButton

x

MultiplyButton

8

EightButton

DivideButton

9

NineButton

0

ZeroButton

Change the font size on the plus, minus, multiply, and divide buttons to 12 points, and then finish up by setting each control s TabStop property to false. Setting TabStop to false in all the controls (TextBox included) prevents the Tab key from moving the input focus around the form. Sometimes it makes sense to support tabbing between controls. But in step 7, you ll endow NetCalc with a separate keyboard interface that s vastly superior to one that merely passes around the input focus.

You just finished designing NetCalc s user interface. Now it s time to add the logic that makes NetCalc behave like a calculator.

Figure 4-25

The NetCalc form in Visual Studio .NET s forms designer.

Step 3: Add Fields

Add the following private fields to the Form1 class. Form1 represents the form you designed in step 2. It was created automatically when the project was created, and it derives from System.Windows.Forms.Form:

Type

Name

Initial Value

Stack

RegStack

new Stack ()

string

FormatString

f2

bool

FixPending

False

bool

DecimalInString

False

bool

EntryInProgress

False

const int

MaxChars

21

You can add fields to Form1 by hand, or you can let Visual Studio .NET add them for you. Simply click the Class View tab that appears (by default) near the right edge of the Visual Studio .NET window to display a list of the classes in your project. Then right-click Form1 and select the Add/Add Field command from the ensuing context menu. When the Add Field wizard appears, fill it in with information about the field you want to add. Here s what the statements that declare and initialize the fields should look like when you ve finished:

private Stack RegStack = new Stack (); private string FormatString = "f2"; private bool FixPending = false; private bool DecimalInString = false; private bool EntryInProgress = false; private const int MaxChars = 21;

If you use the Add Field wizard to create these statements, you ll have to edit them by hand to add the member initializers for example, "= false". The Add Field wizard won t add member initializers to fields that aren t const.

Incidentally, RegStack is an instance of System.Collections.Stack. It represents the calculator s internal register stack. We ll use its Push and Pop methods to move values on and off the stack, just like a real RPN calculator. You don t have to add a

using System.Collections;

statement to Form1.cs because Visual Studio .NET added it for you.

Step 4: Modify the Form s Class Constructor

Open Form1.cs and scroll down until you find Form1 s class constructor. Inside that constructor is the following comment:

// // TODO: Add any constructor code after InitializeComponent call //

Add the following statements after the comment:

RegStack.Push (0.0); DisplayXRegister ();

The Push statement initializes the calculator s internal stack by pushing a 0 onto it. The second statement initializes the calculator s display by copying the value in the X register (the uppermost item on the stack) to the TextBox control.

Step 5: Add Helper Methods

Add the following helper methods to the Form1 class:

Method Name

Return Type

Parameter List

ProcessDigit

void

int Value

Reset

void

None

ConditionalResetDisplay

void

None

InitializeXRegisterFromDisplay

void

None

DisplayXRegister

void

None

ReportError

void

string Message

You can add these methods by hand (in Form1.cs), or you can add them by right-clicking Form1 in the Class View window and selecting the Add/Add Method command. Make all of the methods private. Fill in the method bodies with the code shown for the finished methods in Figure 4-26. If you use the Add Method command to add these methods, Visual Studio .NET adds empty method bodies for you. It s still up to you to enter code between the curly braces.

Step 6: Add Click Handlers

Go back to the forms designer and, one by one, double-click (with the left mouse button) the form s 19 push buttons. Each double-click adds a Click event handler to Form1 and wires the handler to a button. The handlers are named OneButton_Click, TwoButton_Click, and so on. Fill in the empty method bodies with the code in Figure 4-26.

Step 7: Add Keyboard Handlers

NetCalc is now a functional application, but it lacks a keyboard interface. A usable calculator applet must support keystroke entry. To that end, go back to the forms designer and change the form s KeyPreview property from false to true. Turning KeyPreview on enables the form to see keystrokes before its controls do, even if one of its controls has the input focus.

Next override the following methods in Form1 to trap keyboard events:

Method Name

Return Type

Parameter List

OnKeyPress

void

KeyPressEventArgs e

OnKeyDown

void

KeyEventArgs e

As usual, you can add these methods by hand, or you can add them with the Add Method command. Mark all of these methods protected. If you use Add Method, be sure to check the Override box to include the override keyword in the method declaration. Finish up by implementing these methods as you did the ones shown in Figure 4-26.

Step 8: Override ProcessDialogKey

If you were to build and run NetCalc right now, you d find that the keyboard interface works only if you use the keyboard exclusively. For example, if you click the 2 button with the mouse and then press Enter on the keyboard, another 2 appears in the calculator window. Why? Because the click sets the input focus to the 2 button, and when a button has the input focus, the system interprets the Enter key to mean that the button should be clicked.

This problem is solved by overriding an obscure method that Form1 inherits from Form. The method is named ProcessDialogKey. It s called by the framework when certain keys (such as Enter) are pressed to give the form a chance to process the keystroke. Your job is to override the method like this:

protected override bool ProcessDialogKey(Keys keyData) { return keyData == Keys.Enter ? false : base.ProcessDialogKey (keyData); }

This implementation of ProcessDialogKey performs default processing on keys other than Enter by calling the base class s implementation of ProcessDialogKey. When the Enter key is pressed, however, ProcessDialogKey refrains from calling the base class to prevent the system from grabbing the keystroke. The Enter key becomes just another key on the keyboard, and pressing it activates your OnKeyDown handler even if a control currently has the input focus.

Step 9: Build and Run the Application

Build the application by selecting the Build command from Visual Studio .NET s Build menu. Run it by selecting the Start Without Debugging command from the Debug menu (or invoking its keyboard equivalent, Ctrl+F5). Verify that the code is working properly by adding, subtracting, multiplying, and dividing a few numbers. Also click the Fix button followed by the 4 button to change the calculator s display precision from two places to four. If four numbers appear to the right of the decimal point, you re set.

The NetCalc Source Code

The finished version of Form1.cs is shown in Figure 4-26. The lines that I typed in are shown in boldface type; all others were generated by Visual Studio .NET. I won t take the time to analyze each and every line, but I will point out a few highlights:

  • Near the top of the file you ll find the statements that declare instances of Button and TextBox as private fields. These statements were added when you added the controls to the form in the forms designer.

  • The wizard-generated portion of the form s class constructor contains a call to a local method named InitializeComponent. InitializeComponent instantiates the Button and TextBox controls and initializes their property values. Much of this code was added when you added the controls to the form. The remainder was added when you used the Properties window to edit the controls properties.

  • InitializeComponent also includes statements that wire the Button controls to Click handlers. Here is one such statement:

    this.MultiplyButton.Click += new System.EventHandler(this.MultiplyButton_Click);

    These statements were added when you double-clicked the buttons in the forms designer window.

  • Near the end of InitializeComponent is a single statement that adds all the controls to the form. Rather than call Add repeatedly, the forms designer adds the controls to the form in one fell swoop by calling AddRange on the form s Controls collection.

  • The Click handlers for the calculator s numeric buttons call a helper method named ProcessDigit. That method either adds a digit to the number shown in the calculator window or changes the display precision. Clicking the Fix button toggles an internal flag named FixPending indicating whether the next number entered represents the desired precision or a numeric input.

  • Clicking the Enter button activates EnterButton_Click, which pushes the value currently displayed in the calculator window onto the calculator s internal stack.

  • Clicking any of the arithmetic buttons (plus, minus, multiply, or divide) applies the specified operation to the first two values popped off the stack. If one of these buttons is pressed and the value in the calculator window isn t currently on the stack, it s pushed onto the stack so that it can serve as an operand in the forthcoming calculation.

Visual Studio .NET is a big help in building form-based applications such as NetCalc because it reduces the tedium of sizing and positioning controls and assigning values to control properties. Less tedium means shorter development cycles and faster times to market. That s a winning proposition no matter how you look at it.

Form1.cs

using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace NetCalc { /// <summary> /// Summary description for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.Button FixButton; private System.Windows.Forms.Button EnterButton; private System.Windows.Forms.TextBox Display; private System.Windows.Forms.Button ClearButton; private System.Windows.Forms.Button SubtractButton;

 private System.Windows.Forms.Button SevenButton; private System.Windows.Forms.Button EightButton; private System.Windows.Forms.Button NineButton; private System.Windows.Forms.Button FiveButton; private System.Windows.Forms.Button FourButton; private System.Windows.Forms.Button AddButton; private System.Windows.Forms.Button SixButton; private System.Windows.Forms.Button ThreeButton; private System.Windows.Forms.Button MultiplyButton; private System.Windows.Forms.Button OneButton; private System.Windows.Forms.Button TwoButton; private System.Windows.Forms.Button ZeroButton; private System.Windows.Forms.Button DivideButton; private System.Windows.Forms.Button DeleteButton; private System.Windows.Forms.Button DecimalButton; /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // RegStack.Push (0.0); DisplayXRegister (); } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.FixButton = new System.Windows.Forms.Button(); this.EnterButton = new System.Windows.Forms.Button(); this.Display = new System.Windows.Forms.TextBox(); this.ClearButton = new System.Windows.Forms.Button(); this.SubtractButton = new System.Windows.Forms.Button(); this.SevenButton = new System.Windows.Forms.Button(); this.EightButton = new System.Windows.Forms.Button(); this.NineButton = new System.Windows.Forms.Button(); this.FiveButton = new System.Windows.Forms.Button(); this.FourButton = new System.Windows.Forms.Button(); this.AddButton = new System.Windows.Forms.Button(); this.SixButton = new System.Windows.Forms.Button(); this.ThreeButton = new System.Windows.Forms.Button(); this.MultiplyButton = new System.Windows.Forms.Button(); this.OneButton = new System.Windows.Forms.Button(); this.TwoButton = new System.Windows.Forms.Button(); this.ZeroButton = new System.Windows.Forms.Button(); this.DivideButton = new System.Windows.Forms.Button(); this.DeleteButton = new System.Windows.Forms.Button(); this.DecimalButton = new System.Windows.Forms.Button(); this.SuspendLayout(); // // FixButton // this.FixButton.Location = new System.Drawing.Point(112, 57); this.FixButton.Name = "FixButton"; this.FixButton.Size = new System.Drawing.Size(40, 32); this.FixButton.TabIndex = 14; this.FixButton.TabStop = false; this.FixButton.Text = "Fix"; this.FixButton.Click += new System.EventHandler(this.FixButton_Click); // // EnterButton // this.EnterButton.Location = new System.Drawing.Point(16, 57); this.EnterButton.Name = "EnterButton"; this.EnterButton.Size = new System.Drawing.Size(88, 32); this.EnterButton.TabIndex = 15; this.EnterButton.TabStop = false; this.EnterButton.Text = "Enter"; this.EnterButton.Click += new System.EventHandler(this.EnterButton_Click); // // Display // this.Display.BackColor = System.Drawing.SystemColors.Window; this.Display.Cursor = System.Windows.Forms.Cursors.Default; this.Display.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); this.Display.Location = new System.Drawing.Point(16, 17); this.Display.Name = "Display"; this.Display.ReadOnly = true; this.Display.Size = new System.Drawing.Size(184, 22); this.Display.TabIndex = 13; this.Display.TabStop = false; this.Display.Text = "0.00"; this.Display.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; // // ClearButton // this.ClearButton.Location = new System.Drawing.Point(160, 57); this.ClearButton.Name = "ClearButton"; this.ClearButton.Size = new System.Drawing.Size(40, 32); this.ClearButton.TabIndex = 11; this.ClearButton.TabStop = false; this.ClearButton.Text = "Clear"; this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click); // // SubtractButton // this.SubtractButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); this.SubtractButton.Location = new System.Drawing.Point(16, 97); this.SubtractButton.Name = "SubtractButton"; this.SubtractButton.Size = new System.Drawing.Size(40, 32); this.SubtractButton.TabIndex = 12; this.SubtractButton.TabStop = false; this.SubtractButton.Text = "-"; this.SubtractButton.Click += new System.EventHandler(this.SubtractButton_Click); // // SevenButton // this.SevenButton.Location = new System.Drawing.Point(64, 97); this.SevenButton.Name = "SevenButton"; this.SevenButton.Size = new System.Drawing.Size(40, 32); this.SevenButton.TabIndex = 19; this.SevenButton.TabStop = false; this.SevenButton.Text = "7"; this.SevenButton.Click += new System.EventHandler(this.SevenButton_Click); // // EightButton // this.EightButton.Location = new System.Drawing.Point(112, 97); this.EightButton.Name = "EightButton"; this.EightButton.Size = new System.Drawing.Size(40, 32); this.EightButton.TabIndex = 20; this.EightButton.TabStop = false; this.EightButton.Text = "8"; this.EightButton.Click += new System.EventHandler(this.EightButton_Click); // // NineButton // this.NineButton.Location = new System.Drawing.Point(160, 97); this.NineButton.Name = "NineButton"; this.NineButton.Size = new System.Drawing.Size(40, 32); this.NineButton.TabIndex = 18; this.NineButton.TabStop = false; this.NineButton.Text = "9"; this.NineButton.Click += new System.EventHandler(this.NineButton_Click); // // FiveButton // this.FiveButton.Location = new System.Drawing.Point(112, 137); this.FiveButton.Name = "FiveButton"; this.FiveButton.Size = new System.Drawing.Size(40, 32); this.FiveButton.TabIndex = 16; this.FiveButton.TabStop = false; this.FiveButton.Text = "5"; this.FiveButton.Click += new System.EventHandler(this.FiveButton_Click); // // FourButton // this.FourButton.Location = new System.Drawing.Point(64, 137); this.FourButton.Name = "FourButton"; this.FourButton.Size = new System.Drawing.Size(40, 32); this.FourButton.TabIndex = 17; this.FourButton.TabStop = false; this.FourButton.Text = "4"; this.FourButton.Click += new System.EventHandler(this.FourButton_Click); // // AddButton // this.AddButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); this.AddButton.Location = new System.Drawing.Point(16, 137); this.AddButton.Name = "AddButton"; this.AddButton.Size = new System.Drawing.Size(40, 32); this.AddButton.TabIndex = 4; this.AddButton.TabStop = false; this.AddButton.Text = "+"; this.AddButton.Click += new System.EventHandler(this.AddButton_Click); // // SixButton // this.SixButton.Location = new System.Drawing.Point(160, 137); this.SixButton.Name = "SixButton"; this.SixButton.Size = new System.Drawing.Size(40, 32); this.SixButton.TabIndex = 5; this.SixButton.TabStop = false; this.SixButton.Text = "6"; this.SixButton.Click += new System.EventHandler(this.SixButton_Click); // // ThreeButton // this.ThreeButton.Location = new System.Drawing.Point(160, 177); this.ThreeButton.Name = "ThreeButton"; this.ThreeButton.Size = new System.Drawing.Size(40, 32); this.ThreeButton.TabIndex = 3; this.ThreeButton.TabStop = false; this.ThreeButton.Text = "3"; this.ThreeButton.Click += new System.EventHandler(this.ThreeButton_Click); // // MultiplyButton // this.MultiplyButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); this.MultiplyButton.Location = new System.Drawing.Point(16, 177); this.MultiplyButton.Name = "MultiplyButton"; this.MultiplyButton.Size = new System.Drawing.Size(40, 32); this.MultiplyButton.TabIndex = 1; this.MultiplyButton.TabStop = false; this.MultiplyButton.Text = "x"; this.MultiplyButton.Click += new System.EventHandler(this.MultiplyButton_Click); // // OneButton // this.OneButton.Location = new System.Drawing.Point(64, 177); this.OneButton.Name = "OneButton"; this.OneButton.Size = new System.Drawing.Size(40, 32); this.OneButton.TabIndex = 2; this.OneButton.TabStop = false; this.OneButton.Text = "1"; this.OneButton.Click += new System.EventHandler(this.OneButton_Click); // // TwoButton // this.TwoButton.Location = new System.Drawing.Point(112, 177); this.TwoButton.Name = "TwoButton"; this.TwoButton.Size = new System.Drawing.Size(40, 32); this.TwoButton.TabIndex = 9; this.TwoButton.TabStop = false; this.TwoButton.Text = "2"; this.TwoButton.Click += new System.EventHandler(this.TwoButton_Click); // // ZeroButton // this.ZeroButton.Location = new System.Drawing.Point(64, 217); this.ZeroButton.Name = "ZeroButton"; this.ZeroButton.Size = new System.Drawing.Size(40, 32); this.ZeroButton.TabIndex = 10; this.ZeroButton.TabStop = false; this.ZeroButton.Text = "0"; this.ZeroButton.Click += new System.EventHandler(this.ZeroButton_Click); // // DivideButton // this.DivideButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0))); this.DivideButton.Location = new System.Drawing.Point(16, 217); this.DivideButton.Name = "DivideButton"; this.DivideButton.Size = new System.Drawing.Size(40, 32); this.DivideButton.TabIndex = 8; this.DivideButton.TabStop = false; this.DivideButton.Text = ""; this.DivideButton.Click += new System.EventHandler(this.DivideButton_Click); // // DeleteButton // this.DeleteButton.Location = new System.Drawing.Point(160, 217); this.DeleteButton.Name = "DeleteButton"; this.DeleteButton.Size = new System.Drawing.Size(40, 32); this.DeleteButton.TabIndex = 6; this.DeleteButton.TabStop = false; this.DeleteButton.Text = "Del"; this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click); // // DecimalButton // this.DecimalButton.Location = new System.Drawing.Point(112, 217); this.DecimalButton.Name = "DecimalButton"; this.DecimalButton.Size = new System.Drawing.Size(40, 32); this.DecimalButton.TabIndex = 7; this.DecimalButton.TabStop = false; this.DecimalButton.Text = "."; this.DecimalButton.Click += new System.EventHandler(this.DecimalButton_Click); // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(218, 266); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.FixButton, this.EnterButton, this.Display, this.ClearButton, this.SubtractButton, this.SevenButton, this.EightButton, this.NineButton, this.FiveButton, this.FourButton, this.AddButton, this.SixButton, this.ThreeButton, this.MultiplyButton, this.OneButton, this.TwoButton, this.ZeroButton, this.DivideButton, this.DeleteButton, this.DecimalButton}); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.KeyPreview = true; this.MaximizeBox = false; this.Name = "Form1"; this.Text = "NetCalc"; this.ResumeLayout(false); } #endregion /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.Run(new Form1()); } private Stack RegStack = new Stack (); private string FormatString = "f2"; private bool FixPending = false; private bool DecimalInString = false; private bool EntryInProgress = false; private const int MaxChars = 21; // // Process a press of one of the numeric buttons. If Fix was // the last button pressed, change the calculator's output // precision by modifying FormatString. Otherwise, reset the // display if necessary and append the number to any numbers // previously entered. // private void ProcessDigit(int Value) { if (FixPending) { FormatString = "f" + Value.ToString (); if (EntryInProgress) InitializeXRegisterFromDisplay (); DisplayXRegister (); FixPending = false; } else { ConditionalResetDisplay (); if (Display.Text.Length < MaxChars) Display.Text += Value.ToString (); } } // // Reset the calculator's internal flags. // private void Reset() { DecimalInString = false; FixPending = false; EntryInProgress = false; } // // Blank out the value displayed in the calculator window // if an entry isn't already in progress. // private void ConditionalResetDisplay() { if (!EntryInProgress) { EntryInProgress = true; Display.Text = ""; } } // // Convert the text in the calculator display to a numeric // value and push it onto the stack. // private void InitializeXRegisterFromDisplay() { double x = (Display.Text.Length == 0 Display.Text == ".") ? 0.0 : Convert.ToDouble (Display.Text); RegStack.Push (x); } // // Show the value at the top of the stack in the calculator // window. Check for overflow and underflow and display an // error message if either has occurred. // private void DisplayXRegister() { double x = (double) RegStack.Peek (); if (x > Double.MaxValue x < Double.MinValue) ReportError ("Overflow"); else { string display = x.ToString (FormatString); if (display.Length > MaxChars (Math.Abs (x) > 0.0 && Math.Abs (x) < 0.0000000001)) display = x.ToString ("e"); Display.Text = display; } } // // Report an error to the user by displaying an error // message in the calculator window. Also reset the // calculator's internal state so the next button press // will start things over again. // private void ReportError(string Message) { Display.Text = Message; RegStack.Clear (); RegStack.Push (0.0); Reset (); } // // Handlers for the calculator's numeric buttons (0-9). // private void ZeroButton_Click(object sender, System.EventArgs e) { ProcessDigit (0); } private void OneButton_Click(object sender, System.EventArgs e) { ProcessDigit (1); } private void TwoButton_Click(object sender, System.EventArgs e) { ProcessDigit (2); } private void ThreeButton_Click(object sender, System.EventArgs e) { ProcessDigit (3); } private void FourButton_Click(object sender, System.EventArgs e) { ProcessDigit (4); } private void FiveButton_Click(object sender, System.EventArgs e) { ProcessDigit (5); } private void SixButton_Click(object sender, System.EventArgs e) { ProcessDigit (6); } private void SevenButton_Click(object sender, System.EventArgs e) { ProcessDigit (7); } private void EightButton_Click(object sender, System.EventArgs e) { ProcessDigit (8); } private void NineButton_Click(object sender, System.EventArgs e) { ProcessDigit (9); } // // Handlers for the Add, Subtract, Multiply, and // Divide buttons. // // General strategy employed by all four handlers: // 1) Push the value in the calculator display onto the // stack if it hasn't been pushed already. // 2) Pop two values from the stack to use as operands. // 3) Compute the sum, difference, product, or quotient. // 4) Push the result onto the stack. // 5) Display the result. // private void SubtractButton_Click(object sender, System.EventArgs e) { if (EntryInProgress) InitializeXRegisterFromDisplay (); if (RegStack.Count >= 2) { double op1 = (double) RegStack.Pop (); double op2 = (double) RegStack.Pop (); RegStack.Push (op2 - op1); DisplayXRegister (); Reset (); } } private void AddButton_Click(object sender, System.EventArgs e) { if (EntryInProgress) InitializeXRegisterFromDisplay (); if (RegStack.Count >= 2) { double op1 = (double) RegStack.Pop (); double op2 = (double) RegStack.Pop (); RegStack.Push (op2 + op1); DisplayXRegister (); Reset (); } } private void MultiplyButton_Click(object sender, System.EventArgs e) { if (EntryInProgress) InitializeXRegisterFromDisplay (); if (RegStack.Count >= 2) { double op1 = (double) RegStack.Pop (); double op2 = (double) RegStack.Pop (); RegStack.Push (op2 * op1); DisplayXRegister (); Reset (); } } private void DivideButton_Click(object sender, System.EventArgs e) { if (EntryInProgress) InitializeXRegisterFromDisplay (); if (RegStack.Count >= 2) { if ((double) RegStack.Peek () == 0.0) ReportError ("Divide by zero"); else { double op1 = (double) RegStack.Pop (); double op2 = (double) RegStack.Pop (); RegStack.Push (op2 / op1); DisplayXRegister (); Reset (); } } } // // Handler for the Enter button. // private void EnterButton_Click(object sender, System.EventArgs e) { InitializeXRegisterFromDisplay (); DisplayXRegister (); Reset (); } // // Handler for the Fix button. // private void FixButton_Click(object sender, System.EventArgs e) { FixPending = !FixPending; } // // Handler for the Clear button. // private void ClearButton_Click(object sender, System.EventArgs e) { RegStack.Clear (); RegStack.Push (0.0); DisplayXRegister (); Reset (); } // // Handler for the Delete button. // private void DeleteButton_Click(object sender, System.EventArgs e) { int len = Display.Text.Length; if (len > 0 && EntryInProgress) { if (Display.Text[len - 1] == '.') DecimalInString = false; Display.Text = Display.Text.Substring (0, len - 1); } } // // Handler for the '.' button. // private void DecimalButton_Click(object sender, System.EventArgs e) { ConditionalResetDisplay (); if (Display.Text.Length < MaxChars && !DecimalInString) { Display.Text += "."; DecimalInString = true; } } // // Handler for KeyPress events. Comprises half of NetCalc's // keyboard interface. // protected override void OnKeyPress(KeyPressEventArgs e) { switch (e.KeyChar) { case '0': ZeroButton_Click (ZeroButton, new EventArgs ()); break; case '1': OneButton_Click (OneButton, new EventArgs ()); break; case '2': TwoButton_Click (TwoButton, new EventArgs ()); break; case '3': ThreeButton_Click (ThreeButton, new EventArgs ()); break; case '4': FourButton_Click (FourButton, new EventArgs ()); break; case '5': FiveButton_Click (FiveButton, new EventArgs ()); break; case '6': SixButton_Click (SixButton, new EventArgs ()); break; case '7': SevenButton_Click (SevenButton, new EventArgs ()); break; case '8': EightButton_Click (EightButton, new EventArgs ()); break; case '9': NineButton_Click (NineButton, new EventArgs ()); break; case '.': DecimalButton_Click (DecimalButton, new EventArgs ()); break; case '-': SubtractButton_Click (SubtractButton, new EventArgs ()); break; case '+': AddButton_Click (AddButton, new EventArgs ()); break; case '*': MultiplyButton_Click (MultiplyButton, new EventArgs ()); break; case '/': DivideButton_Click (DivideButton, new EventArgs ()); break; } } // // Handler for KeyDown events. The other half of NetCalc's // keyboard interface. // protected override void OnKeyDown(KeyEventArgs e) { switch (e.KeyCode) { case Keys.C: ClearButton_Click (ClearButton, new EventArgs ()); break; case Keys.F: FixButton_Click (FixButton, new EventArgs ()); break; case Keys.Enter: EnterButton_Click (EnterButton, new EventArgs ()); break; case Keys.Delete: DeleteButton_Click (DeleteButton, new EventArgs ()); break; } } // // The following override prevents the form from "stealing" // the Enter key and using it to simulate button clicks. // protected override bool ProcessDialogKey(Keys keyData) { return keyData == Keys.Enter ? false : base.ProcessDialogKey (keyData); } } }

Figure 4-26

The NetCalc source code.



Programming Microsoft  .NET
Applied MicrosoftNET Framework Programming in Microsoft Visual BasicNET
ISBN: B000MUD834
EAN: N/A
Year: 2002
Pages: 101

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net