VB.Net: OOP Calculator - calc2
The purposes of this project was to create an OOP Calculator. More precisely, the Requirements Specification is:
The user should be able to implement a calculation consisting of performing a mathematical operation on two numbers and retrieve a result*.*
The Core Application
The words underlined were:
- **Calculation - **The Core classes of the application should include a Calculation class used for communicating between the core classes and the GUI.
- **Operation - **The Operator would consist of a one character variable in the Calculation class and there is no reason to make it a class.
- **Two numbers - **The two numbers involved in the calculation would both be represented by an instance of a Number class as it is appropriate to represent each number by a Decimal value and a String value, mainly due to the method of inputting numbers. It would also be useful to have methods to append and remove digits in this class.
- **Result - **As with the Operator there is no reason to make this a class, but also in this case it is not necessary even as a variable either.
** Figure 1. **The method used for saving
the result allows long strings of multiple calculations.
So after analyzing the project requirements the core application consists of just two classes.
Figure 1.1. The distinct core classes
A Calculation class with several properties used in differentiating between various input values, with one Public function the push method which is used for passing and retrieving values to and from the GUI. The Calculation class has three notable properties, two to represent Number class instances and an [Operator] property. I haven't posted the Calculation class in this article, but it's available in the download.
This shows the instances of the classes and the Relationship between them:
Figure 1.2. Instances and Relationship
Figure 1.3. Core classes diagram
The Number class has a Decimal property and a String property, both of which are used to keep track of numbers and two functions for adding and removing digits.
By some juggling of the two Number classes property values it's possible to perform long strings of multiple calculations (see Figure 1).
The Number class:
Public Class Number
#Region " properties"
Private _value As Decimal
Public Property value() As Decimal
Get
Return _value
End Get
Set ( ByVal value As Decimal)
_value = value
End Set
End Property
Private _valueString As String = ""
Public Property valueString() As String
Get
If _valueString.StartsWith(".") Then
Return "0" & _valueString
End If
Return _valueString
End Get
Set ( ByVal value As String)
_valueString = value
If _valueString <> "." AndAlso _valueString <> "" AndAlso _valueString <> "-" Then
Me .value = CDec (_valueString)
Else
Me .value = 0D
End If
End Set
End Property
#End Region
#Region " functions"
''' <summary>
''' handles parsing button input to decimal
''' </summary>
''' <param name="aValue">the button text</param>
''' <returns></returns>
Public Function appendDigit(aValue As String) As String
If aValue = "." Then
If Not Me.valueString.Contains(".") Then
Me .valueString &= aValue
End If
Else
Me .valueString &= aValue
End If
If Not valueString = "." AndAlso _valueString <> "-" Then
Me .value = CDec (valueString)
Else
Me .value = 0
End If
Return Me.valueString
End Function
''' <summary>
''' handles removing digits with the backspace button,
''' then parsing the remaining digits as decimal
''' </summary>
''' <returns></returns>
Public Function backSpace() As String
If Me.valueString.Length > 0 Then
Me .valueString = Me .valueString.Substring(0, valueString.Length - 1)
End If
If Not Me.valueString = "." AndAlso Not Me.valueString = "" AndAlso Not Me.valueString = "-" Then
Me .value = CDec ( Me .valueString)
Else
Me .value = 0
End If
Return Me.valueString
End Function
#End Region
End Class
The GUI
Figure 2. The GUI.
In terms of component controls the GUI consists of a Panel, 18 Buttons, an extended TextBox, and a Label. At runtime the TextBox and the Label appear as one control (see Figure 1.). The TextBox is restricted to allow no other input except the programmatic input from the Core classes.
The Form code is limited to handling the Panel_Paint event where it draws a border around the Label and TextBox, handling all of the Button click events in one handler, and capturing keyboard input. The 18 Buttons are the only Controls that respond to user input, either through mouseclicks or by typing the number, operator, or controlchar keys.
This is the Form code:
Public Class calcForm
''' <summary>
''' this is the calculator object
''' </summary>
Private calc As New Calculation
''' <summary>
''' the textbox is actually a textbox with a label above it.
''' this draws a border around both controls to give the appearance of one control
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
Dim r As New Rectangle(TextBox1.Left - 1, Label1.Top - 1, Label1.Width + 2, Label1.Height + TextBox1.Height + 2)
ControlPaint.DrawBorder3D(e.Graphics, r, Border3DStyle.SunkenInner)
End Sub
''' <summary>
''' this sets up the buttons for keyboard or mouseclick input
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.KeyPreview = True
For Each b As Button In Panel1.Controls.OfType(Of Button)
AddHandler b.Click, AddressOf button_clicked
Next
End Sub
''' <summary>
''' onclick the button's text is sent to the calculator object's push method
''' which handles all input, and returns the textual output for the textbox and the label
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub button_clicked(sender As Object, e As EventArgs)
Dim output() As String = calc.push(DirectCast(sender, Button).Text)
TextBox1.Text = output(0)
Label1.Text = output(1)
End Sub
''' <summary>
''' this converts keyboard input into button clicks
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub Form1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
If e.KeyChar = Chr(Keys.Back) Then
Button1.PerformClick()
Else
Dim btn As Button = Panel1.Controls.OfType(Of Button).FirstOrDefault(Function(b) b.Text.ToLower = e.KeyChar)
If Not btn Is Nothing Then
btn.PerformClick()
End If
End If
End Sub
End Class
The extended TextBox assigns a new empty ContextMenuStrip to the TextBox, which has the effect of removing the right click menu. The Cut, Copy, Paste, Clear, and Undo keyboard shortcuts are also detected and nullified.
This is the restrictedTextBox code:
''' <summary>
''' removes the textbox contextmenustrip and disables keyboard shortcuts
''' </summary>
Public Class restrictedTextBox
Inherits TextBox
Const WM_CUT As Integer = &H300
Const WM_COPY As Integer = &H301
Const WM_PASTE As Integer = &H302
Const WM_CLEAR As Integer = &H303
Const WM_UNDO As Integer = &H304
Public Sub New()
Me.ContextMenuStrip = New ContextMenuStrip
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Dim disabledOperations() As Integer = {WM_CUT, WM_COPY, WM_PASTE, WM_CLEAR, WM_UNDO}
If disabledOperations.Contains(m.Msg) Then Return
MyBase.WndProc(m)
End Sub
End Class
Other Resources
Full article and download here...