Is this program efficient - vb.net

I've put together a small calculator application that works quite well, but despite being a novice to VB.net I know that the program probably isn't as efficient as it should be. The idea is that upon inputting numbers into a textbox and pressing a mathematical operator, the textbox will reset and continue the equation, storing the past values entered.
Dim input1 As Double
Dim numfunction As Double
'numerical functions (null = 0, add = 1, subtract = 2, divide = 3, multiply = 4)
Private Sub btnAdd_Click(sender As Object, e As RoutedEventArgs) Handles btnAdd.Click
If txtNum.Text = "" Then
MsgBox("Please enter a number")
Else
numfunction = 1
input1 = input1 + txtNum.Text
txtNum.Text = ""
End If
End Sub
Private Sub btnEqual_Click(sender As Object, e As RoutedEventArgs) Handles btnEqual.Click
If txtNum.Text = "" Then
MsgBox("Please enter a final number")
End If
If numfunction = 1 Then
txtNum.Text = txtNum.Text + input1
input1 = 0
End If
End Sub
Could you point me in the right direction as to what I should replace add or remove to make my programs more efficient in the future? Keep in mind that the BtnAdd_Click event is just one of 4 (add, sub, divide, multiply) and because of that btnEqual_Click will have a few if statements, checking for what function the user has put in, and if there is anything in txtNum at all.
Thanks in advance, I'm not asking for anyone to complete my code, but I'd love to see what options I have so I make more efficient programs in the future.

With a little initial effort you could simplify this task by using the power of object oriented programming:
Public Class Form1
' hold a reference to all operations in a list of operations
Private _operations As New List(Of Operation)
' the operation currently choosen
Private Property _currentOperation As Operation
' the 2 numbers you want to perform the operations on
Private _number1 As Double = 0
Private _number2 As Double = 0
Public Sub New()
InitializeComponent()
SetupOperations()
TextBox1.Text = 0
End Sub
Public Sub ChangeOperation(operation As Operation)
_number1 = _currentOperation.FunctionDelegate.Invoke(_number1, _number2)
TextBox1.Text = _number1
_currentOperation = operation
End Sub
Private Sub SetupOperations()
_operations.Add(New Operation(Me, btnAdd, Function(x, y)
Return x + y
End Function))
' heres the crux ... you use anonymous method to define your functions hook them to the form (Me) and the related Button
' all at once
' Similar for the other operations (subtract / multiply / divide / pow, and so on)
Dim equalsOperation As New Operation(Me, btnEqual, Function(x, y)
Return y
End Function)
_operations.Add(equalsOperation)
_currentOperation = equalsOperation
End Sub
' for this example i used only one textbox and a lable indicating wheter the number entered is a valid double
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
Dim result As Double
If Double.TryParse(TextBox1.Text, result) Then
_number2 = result
lblValid.Text = "Valid" ' tell the user that the number entered is valid or not
Else
lblValid.Text = "Invalid"
End If
End Sub
''' <summary>
''' An Operation that hooks up a button click and can execute the operation
''' </summary>
Public Class Operation
Private _owningForm As Form1
Public Property FunctionDelegate As Func(Of Double, Double, Double) ' use a delegate to a Func that returns double with 2 double parameters
Public Sub New(owningForm As Form1, boundButton As Button, functionDelegate As Func(Of Double, Double, Double))
Me.FunctionDelegate = functionDelegate
Me._owningForm = owningForm
AddHandler boundButton.Click, AddressOf boundButton_Click ' make the operation hook up on the click event
End Sub
Private Sub boundButton_Click()
_owningForm.ChangeOperation(Me)
End Sub
End Class
End Class
Hope this is not too confusing for you, I intended to show you a bigger world than simple routines and tons of eventhandlers

You could simplify your code by inserting your checking code into a separate subroutine.
Sub CheckVals()
If txtNum.Text = "" Then
MsgBox("Please enter a final number")
End If
If numfunction = 1 Then
txtNum.Text = txtNum.Text + input1
input1 = 0
End If
End Sub
You would then reference this sub from your two button click events.
Private Sub btnAdd_Click(sender As Object, e As RoutedEventArgs) Handles btnAdd.Click
CheckVals()
End Sub

Related

Update textbox to a different form from backgroundworker

I have two forms, Form1 and Newform. Form1 has two buttons and a textbox and Newform has its own textbox. I am using a settext sub to invoke a delegate sub in the backgroundworker to update the textbox in both forms.
The textbox in Form1 seems to be updating but the textbox in Newform isn't updating.
Is there something that I'm missing if I want to update the textbox on a different form?
Thanks in advance.
Imports System.Threading
Public Class Form1
Dim stopbit As Boolean
Dim TestingComplete As Boolean
Dim ReadValue As Double
Dim FinalValue As Double
Delegate Sub SetTextCallback(ByRef Txtbox As TextBox, ByVal Txt As String)
'Thread Safe textbox update routine
Private Sub SetText(ByRef Txtbox As TextBox, ByVal Txt As String)
' InvokeRequired required compares the thread ID of the
' calling thread to the thread ID of the creating thread.
' If these threads are different, it returns true.
Console.WriteLine(Txtbox.InvokeRequired & " textbox invokerequired")
If Txtbox.InvokeRequired Then
Try
'MsgBox("inside settext")
Txtbox.Invoke(New SetTextCallback(AddressOf SetText), Txtbox, Txt)
Catch ex As Exception
MsgBox(ex.Message)
End Try
Else
Txtbox.Text = Txt
Txtbox.Update()
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
newform.Show()
End Sub
Function ReadTemp() As Double
ReadValue = ReadValue / 2
Return ReadValue
End Function
Sub Test()
Dim starttime As Integer
Dim EllapsedTime As Integer
Dim OldValue As Double = 0
Dim NewValue As Double = 0
Dim Difference As Double = 1
Dim Margin As Double = 0.1
stopbit = False
starttime = My.Computer.Clock.TickCount
Do
Thread.Sleep(200)
OldValue = NewValue
NewValue = ReadTemp()
Difference = Math.Abs(NewValue - OldValue)
SetText(Me.TextBox1, Difference.ToString)
SetText(newform.TextBox1, Difference.ToString)
newform.Refresh()
EllapsedTime = My.Computer.Clock.TickCount - starttime
Loop Until EllapsedTime > 5000 Or stopbit = True ' Or Difference < Margin
FinalValue = NewValue
TestingComplete = True
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
stopbit = True
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
For i As Integer = 1 To 10
ReadValue = 100000
TestingComplete = False
ThreadPool.QueueUserWorkItem(AddressOf Test)
Do
Thread.Sleep(200)
Loop Until TestingComplete = True
MsgBox("Final Value " & FinalValue)
Next
End Sub
End Class
Your issue is due to that you're using the default instance of newform. In VB.NET default form instances is a feature that allows you to access a form via its type name without having to manually create an instance of it.
In other words it lets you do this:
newform.Show()
newform.TextBox1.Text = "Something"
...instead of doing it the correct way, which is this:
Dim myNewForm As New newform
myNewForm.Show()
myNewForm.TextBox1.Text = "Something"
Above we create a new instance of newform called myNewForm. This is required to be able to use most objects in the framework (including forms). However, VB.NET simplifies this behaviour by offering to create the instance for you, which is what is going on in my first example.
The problem with these default instances is that they are thread-specific, meaning a new instance is created for every thread that you use this behaviour in.
Thus the form you refer to when you do:
newform.Show()
...is not the same form that you refer to in your thread, because a new instance has been created for it in that thread:
'This is not the same "newform" as above!
SetText(newform.TextBox1, Difference.ToString)
The solution to this is of course to create the instance yourself, allowing you to have full control over what's going on:
Dim newFrm As New newform
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
newFrm.Show()
End Sub
...your code...
Sub Test()
...your code...
SetText(newFrm.TextBox1, Difference.ToString)
...even more of your code...
End Sub
As a side note you can remove your calls to newform.Refresh() and Txtbox.Update(). These just cause unnecessary overhead by forcing the form and text boxes to redraw themselves, which is already done when you change any of their properties that affect their contents/design (so you are essentially making them redraw themselves twice).
Also, if you want to make invoking to the UI thread simpler and you are using Visual Studio/Visual Basic 2010 or newer, you could switch to using lambda expressions instead of regular delegates. They're much easier to use and allows you to create whole methods in-line that can be invoked on the UI thread.
For this purpose I've written an extension method called InvokeIfRequired() which lets you invoke any method/function on the UI thread, checking InvokeRequired for you. It's similar to what you have now, only it works for any control (not just text boxes) and with lambda expressions, allows you to run any code you want on the UI.
You can use it by adding a module to your project (Add New Item... > Module) and naming it Extensions. Then put this code inside it:
Imports System.Runtime.CompilerServices
Public Module Extensions
''' <summary>
''' Invokes the specified method on the calling control's thread (if necessary, otherwise on the current thread).
''' </summary>
''' <param name="Control">The control which's thread to invoke the method at.</param>
''' <param name="Method">The method to invoke.</param>
''' <param name="Parameters">The parameters to pass to the method (optional).</param>
''' <remarks></remarks>
<Extension()> _
Public Function InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object()) As Object
If Parameters IsNot Nothing AndAlso _
Parameters.Length = 0 Then Parameters = Nothing
If Control.InvokeRequired = True Then
Return Control.Invoke(Method, Parameters)
Else
Return Method.DynamicInvoke(Parameters)
End If
End Function
End Module
This allows you to invoke either one line of code by doing:
Me.InvokeIfRequired(Sub() Me.TextBox1.Text = Difference.ToString())
Or to invoke a whole block of code by doing:
Me.InvokeIfRequired(Sub()
Me.TextBox1.Text = Difference.ToString()
newFrm.TextBox1.Text = Difference.ToString()
Me.BackColor = Color.Red 'Just an example of what you can do.
End Sub)
YourSubHere
Me.Invoke(Sub()
Form1.Textbox1.text="some text1"
Form2.Textbox2.text="some text2"
End Sub)
End Sub
Or if it's a one liner.
Me.Invoke(Sub() Form1.Textbox1.text="some text1")
Depending on what you need, you could invoke just some control like:
Textbox1.invoke(Sub() Textbox1.text="some text1")

Cannot set focus to textbox

I am using VB and trying to select a portion of the text in a textbox of a separate form. However, I can't seem to find a good way to access the textbox from the other form, although the textbox is public (I am new to VB).
Currently, I'm trying to do this by calling a function located in the form (the form with the textbox), and then focusing on the textbox and selecting/highlighting the text. But it still doesn't work:
Public Sub GetFindLoc(ByVal lngStart As Long, ByVal intLen As Integer)
frmFind.Hide()
MessageBox.Show(ActiveForm.Name)
MessageBox.Show(txtNotes.CanFocus())
txtNotes.Focus()
txtNotes.Select(lngStart, intLen)
frmFind.Show()
End Sub
With this, I first hide the original form, and then try to select the text, and bring back the form. It shows that the active form is the one which I'm trying to select the text on, but it returns false on CanFocus().
Any help would be appreciated, thank you!
Hmm. This was more fiddly than I thought. You need to pass a reference to the other form:
Main form:
Public Class frmNotes
'This is the main form
'This form has a textbox named txtNotes and a button called btnShowFind
'txtNotes has .MultiLine=True
Private mfrmFind As frmFind
Private Sub btnShowFind_Click(sender As Object, e As EventArgs) Handles btnShowFind.Click
If mfrmFind Is Nothing OrElse mfrmFind.IsDisposed Then
mfrmFind = New frmFind(Me)
mfrmFind.Show()
Else
mfrmFind.BringToFront()
End If
End Sub
End Class
Finder form:
Public Class frmFind
'This form has a textbox called txtFind and a button called btnFind
Private mfrmParent As frmNotes
Sub New(parent As frmNotes)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
mfrmParent = parent
End Sub
Private Sub btnFind_Click(sender As Object, e As EventArgs) Handles btnFind.Click
If txtFind.Text = "" Then
MsgBox("Please enter text to find", MsgBoxStyle.Exclamation)
Exit Sub
End If
Dim intSearchBegin As Integer = mfrmParent.txtNotes.SelectionStart + 1
Dim intStart As Integer = mfrmParent.txtNotes.Text.IndexOf(txtFind.Text, intSearchBegin)
If intStart > -1 Then
mfrmParent.txtNotes.Select(intStart, txtFind.Text.Length)
mfrmParent.txtNotes.Focus()
mfrmParent.BringToFront()
Else
mfrmParent.txtNotes.Select(0, 0)
MsgBox("No more matches")
End If
End Sub
End Class
Public Class frmFind
Private Sub btnFind_Click(sender As Object, e As EventArgs) Handles btnFind.Click
Dim search As String = TextBox1.Text.Trim
Dim pos As Integer = frmNotes.txtNotes.Text.IndexOf(search)
If pos > 0 Then
frmNotes.txtNotes.Focus()
frmNotes.txtNotes.Select(pos, search.Length)
End If
End Sub
End Class
This is just a "find" form with 1 textbox and 1 button which will highlight the first occurrence of the string in TextBox1 that it finds in txtNotes on the other form. If you want it to find whitespace as well, then remove the Trim function. You can add code to find other occurrences or go forward/backward.

Visual basic empty text box throws exception

The code below is a program to calculate the BMI using text boxes. I am having an issue however that when I clear one of the text boxes it will throw an exception and freeze the program. I was wondering if anyone had an answer on how to prevent this. I already tried setting my variables to 0 and 1 to see if that was the issue but it does not appear to be.
Private Sub tboxWeight_TextChanged(sender As Object, e As EventArgs) Handles tboxWeight.TextChanged
Weight = 0
Weight = Convert.ToInt64(tboxWeight.Text)
End Sub
Private Sub tboxHFeet_TextChanged(sender As Object, e As EventArgs) Handles tboxHFeet.TextChanged
Height_feet = 0
Height_feet = Convert.ToInt64(tboxHFeet.Text)
Get_BMI(1)
End Sub
Private Sub tboxHInch_TextChanged(sender As Object, e As EventArgs) Handles tboxHInch.TextChanged
Height_Inches = 0
Height_Inches = Convert.ToInt64(tboxHInch.Text)
Get_BMI(1)
End Sub
Private Sub tboxAge_TextChanged(sender As Object, e As EventArgs) Handles tboxAge.TextChanged
Age = Convert.ToDouble(tboxAge.Text)
End Sub
Function Get_BMI(ByVal j As Integer) As Double
BMI = (Weight / (Height_Inches + (Height_feet * 12) ^ 2) * 703)
tboxBMI.Text = Convert.ToString(BMI)
Exit Function
End function
It is because you set a textbox into an integer field, so when the textbox is empty it will throw exception because the textbox doesn't contain a number.
Try using If else statement for each textboxes.
String.IsNullOrEmpty function will be sufficient.
Good/Best practice says, you need to validate the data before performing calculation i.e. Get_BMI(). Below code snippet will help you.
Dim textBoxValue As String
If Not String.IsNullOrEmpty(textBoxValue) Then
If IsNumeric(textBoxValue) Then
End If
End If

Combo Box Returning -1 For SelectedIndex

I'm trying to pick up a combo box's selected index. This was working absolutely fine, then all of a sudden it started returning -1 no matter what item is selected
My code is:
Form Code
Private Sub Man_SelectedIndexChanged_1(sender As Object, e As EventArgs) Handles Man.SelectedIndexChanged, Units.SelectedIndexChanged
'Set Transducer Type
Call References.LevListAdd()
End Sub
References Module LevListAdd Sub
Public Sub LevListAdd()
Form1.Lev.Items.Clear()
If Form1.Man.Text = "Pulsar" Then
With Form1.Lev.Items
.Add("Ultra Range")
.Add("IMP Range")
.Add("Twin Range")
End With
End If
End Sub
This fills the combo box lev fine when the Man combo box item "Pulsar" is selected. I then want my users to click a button to generate some labels and stuff. The code is as such:
Button Click Code
Private Sub Generate_Click(sender As Object, e As EventArgs) Handles Generate.Click
If CheckGenerate() = False Then Exit Sub
Dim X = CheckGenerationType(Man.SelectedIndex, Lev.SelectedIndex, Level.Checked, Volume.Checked, ListBox1.SelectedIndex,
Units.SelectedIndex)
Call ParamPage(X)
End Sub
CheckGenerate simply checks that all boxes have been filled in. I pass the informtion from the form to the following function:
Public Function CheckGenerationType(Man As Integer, Lev As Integer, Level As Boolean, Volume As Boolean, TankType As Integer,
MeasurementUnit As Integer) As String
Dim M As Integer
Dim L As Integer
Dim CT As Integer
Dim TT As Integer
Dim Ms As Integer
M = Man
L = Lev
TT = TankType
Ms = MeasurementUnit
If Level = True Then
CT = 0
ElseIf Volume = True Then
CT = 1
End If
CheckGenerationType = CStr(M) + "." + CStr(L) + "." + CStr(CT) + "." + CStr(TT) + "." + CStr(Ms)
End Function
When the lev.selectedindex parameter arrives at this function, it reads -1. Even if the user has selected any of the 3 items. Can anyone explain why this is happening?
I've just tried your code. I get the same result (-1 in lev.SelectedIndex) and this was because jumped using tab through the controls my when i'm hitting the Man or Units Combobox it runs the LevListAdd() and then clears the Lev-ComboBox because of Form1.Lev.Items.Clear().
You should think about your call Man_SelectedIndexChanged_1 or maybe just use something like this:
Public Sub LevListAdd()
If Form1.Man.Text = "Pulsar" Then
Form1.Lev.Items.Clear()
instead of
Public Sub LevListAdd()
Form1.Lev.Items.Clear()
If Form1.Man.Text = "Pulsar" Then
And you should separate the calls from the man and unit ComboBoxes.
Private Sub Unit_SelectedIndexChanged_1(sender As Object, e As EventArgs) Handles Units.SelectedIndexChanged
' Do something
End Sub
Private Sub Man_SelectedIndexChanged_1(sender As Object, e As EventArgs) Handles Man.SelectedIndexChanged
Form1.Lev.Items.Clear()
Select Case Form1.Man.Text
Case "Pulsar"
With Form1.Lev.Items
.Add("Ultra Range")
.Add("IMP Range")
.Add("Twin Range")
End With
Case "animals"
With Form1.Lev.Items
.Add("dogs")
.Add("cats")
.Add("birds")
End With
End Select
End Sub

Passing arguments to methods in VB

I'm hoping you guys can help with a problem that should be simple to solve, I've just had issues finding a solution. In the program that I'm writing some of the textbox's have to be numeric between 1 and 10, and others just have to be numeric. Instead of coding each textbox to verify these parameters I decided to write methods for each of them. I'm having problems passing the arguments and getting it to function correctly. Included is some of my code that shows what I'm trying to accomplish.
Public Shared Sub checkforonetoten(ByVal onetoten As Double)
If (onetoten > 1 & onetoten < 10) Then
Else
MessageBox.Show("Please enter a Number between 1-10", "Error")
End If
End Sub
Public Shared Sub checkfornumber(numCheck As Double)
Dim numericCheck As Boolean
numericCheck = IsNumeric(numCheck)
If (numericCheck = False) Then
MessageBox.Show("Please enter a number", "Error")
End If
End Sub
Private Sub textboxS_TextChanged(sender As Object, e As EventArgs) Handles textboxS.TextChanged
Dim S As Double
S = textboxS.Text
checkfornumber(S)
checkforonetoten(S)
End Sub
One of your main problems is you're converting your text without validating it. You're also programming without the Options On to warn you of bad conversion techniques like you're using in the event handler.
The TryParse method would come in handy here:
Private Sub textboxS_TextChanged(sender As Object, e As EventArgs) Handles textboxS.TextChanged
Dim S As Double
If Double.TryParse(textboxS.Text, S) Then
checkforonetoten(S)
End If
End Sub
Since the TryParse method validates your text and sets the value to 'S', you only need to check the range.
Of course using NumericUpDown controls would make all this moot, since the values will always only be numbers and you can set the range on each one.
one way to structure it is to have one event procedure process the similar TB types:
Private Sub textboxS_TextChanged(sender As Object, e As EventArgs) _
Handles textbox1.TextChanged, textbox12.TextChanged, _
Handles textbox16.TextChanged
Dim S As Double
If Double.TryParse(Ctype(sender, TextBox).Text, S) Then
' or place the Check code here for all the TextBoxes listed above
checkforonetoten(S)
End If
End Sub
The plain numeric kind:
Private Sub textboxQ_TextChanged(sender As Object, e As EventArgs) _
Handles textbox2.TextChanged, textbox6.TextChanged
Dim S As Double
If Double.TryParse(Ctype(sender, TextBox).Text, S) = False Then
MessageBox.Show("Please enter a number", "Error")
End If
End Sub
Rather than calling a function and passing the current TextBox from events (which is fine), have 2 or 3 events process them all. The point is adding/moving the Handles clause to a common event procedure (be sure to delete the old ones).
If you do decide to call a common function, dont do anything in the events (you still have one per TB) and do it all in the common proc:
Private Sub textboxS_TextChanged(sender As Object, e As EventArgs) _
Handles textboxS.TextChanged
checkforonetoten(Sender)
End Sub
private Sub checkforonetoten(tb As Textbox)
Dim S As Double
If Double.TryParse(tb.Text, S) Then
' your check for 1 - 10 on var S
else
' error: not a valid number
End If
end sub
Also:
If (onetoten > 1 & onetoten < 10) Then
should be:
If (onetoten > 1) AndAlso (onetoten < 10) Then