Problem with a VB function on my form (Visual Studio 2019) - vb.net

The function I am trying to create takes in an input from a combo box (Small, Medium, Large)
and then spits out a price based on what was selected. For example, if the user chooses small the calculated price would be 100, if Medium: 200, and if Large: 300. I want the function to be executed after the user clicks the button I created "Calculate Price". I would then have a message box tell the user "The calculated price is: {result}." I currently just keep getting the output "The calculated price is: 0." upon testing my work. Here is the code I am using for the function and button click. (Note: I don't need to have the dollar amounts be returned as integers, they can just be strings Im not too picky about that)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim result As Integer
result = calcpriceFunction(cboUnitType.ToString)
MessageBox.Show("The calculated price is: " & result)
End Sub
Private Function calcpriceFunction(ByVal untype As String) As Integer
Dim price As String
untype = ""
price = ""
If untype = "Small" Then
Return price = "100"
ElseIf untype = "Medium" Then
Return price = "200"
ElseIf untype = "Large" Then
Return price = "300"
End If
End Function
If anyone can spot a mistake or some sort of convention flaw that would be amazing. I am all out of options right now!

There are many problems with calcpriceFunction
First of all, the code declares that function as returning an integer but instead you try to return a string. This is accepted by the compiler because in the option of your project you have the Option Strict set to Off and this enables automatic type conversion (when possible). While it seems a good thing, in reality this automatic conversion creates lot of problems because you will never know when, for any reason, it doesn't work as expected. So, first thing is to change that option to Option Strict On.
Then it is necessary to remove that untype = "" because it changes the value passed in the parameter.
Finally, don't return at each if/else statement, but just set the price value and use just one final return
Private Function calcpriceFunction(ByVal untype As String) As Integer
' Zero is the default, but I like to be explicit
Dim price As Integer = 0
If untype = "Small" Then
price = 100
ElseIf untype = "Medium" Then
price = 200
ElseIf untype = "Large" Then
price = 300
End If
Return price
End Function
This change will remove the many exit points from your method but also remove another subtle bug present in the original return statement
Return price = "100"
doesn't assign the value "100" to the variable price and then return it, instead it ask the compiler to return the result of the comparison between the current value of price and the constant "100". Or in other words. Is the current value of price equals to "100"? If not return 0

Private Function calculatePrice(untype As String) As Integer
If untype = "Small" Then Return 100
If untype = "Medium" Then Return 200
If untype = "Large" Then Return 300
Throw New ArgumentOutOfRangeException()
End Function
One thing to understand early is that it's very bad to conflate integers and strings. "100" and 100 are not the same value! Moreover, thanks to internationalization/localization and other cultural concerns, converting back and forth between those values is so much slower and error-prone than you can know. It's absolutely something to avoid as much as possible.
Therefore, when accepting numeric input from a user, which of course must start as a string, convert to a numeric type like integer or Decimal as soon as possible, and keep it that way as long as possible, only convert it back to a string again when you must show it to a user again.
The same principle applies to DateTime values.

Here are some modifications to your calcpriceFunction:
Private Function CalculatePrice(untype As String) As Integer
Select Case untype.ToLowerInvariant
Case "small"
Return 100
Case "medium"
Return 200
Case "large"
Return 300
Case Else
Return 0
End Select
End Function
In addition to the issues Steve mentioned in his answer, I suggest you make the function case-insensitive for the untype value. Also, I changed the condition block from If ... ElseIf ... ElseIf ... End If to Select Case for readability. Note that you can return the price directly once you have determined what it is as it is done in this example with Return 100. Finally, I renamed the function to CalculatePrice. There is no reason to abbreviate the name and you should not have Function in a function name.

I made these changes to my Button Click and function and I got my desired outcome! Thank you so much everyone for the help.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim result As Integer
result = calcpriceFunction(cboUnitType.SelectedValue)
MessageBox.Show("The calculated price is: $" & result & " per month.")
End Sub
Private Function calcpriceFunction(ByVal untype As String) As Integer
Dim price As Integer = 0
If untype = "Small" Then
price = 100
ElseIf untype = "Medium" Then
price = 200
ElseIf untype = "Large" Then
price = 300
End If
Return price
End Function

Related

TryParse method is reading negative value and turning it to a positive 0 in VB.Net code

I created this code in order to get the total sum of these values.
Dim budget As Double
If gv_DO_Tasks.Rows.Count() = 0 Then
lblTotalTaskTotalBudget.Text = ""
Return
End If
For Each i As GridViewRow In gv_DO_Tasks.Rows
Dim amount As String = Replace(i.Cells(6).Text, "$", " ")
Dim value As Double
If Double.TryParse(amount, value) = True Then
budget = budget + value
End If
Next
The issue is that I have a negative value as one of the amounts and when I try to do the TryParse method to convert the amount which is a string to a double, the value becomes 0 as a positive integer instead of the previous value which is ($26,652.25) or -$26,652.25 That messes up the total which is supposed to be less. The total sum is showing $989,992.74 but it should be $963,340.49.
Is there anything I have to add to the code or change?
Please help, thank you.
I suspect replacing the dollar sign with a space is leaving you with something like this:
- 26,642.25
Notice the space between the negative sign and the value. If you replace the dollar sign with an empty string instead, I'd bet the code will work:
Dim amount As String = Replace(i.Cells(6).Text, "$", "")
I might also be tempted to write a utility/library method like this:
<Extension()>
Public Shared Iterator Function WhereDoubleParses(Of T)(items As IEnumerable(Of T), selector As Func(Of T, Double)) As IEnumerable(Of Double)
For Each item As T in items
Dim result As Double
If (Double.TryParse(selector(item), result) Then
Yield result
End If
Next
End Function
Which I would then use to reduce the original For loop down to this:
budget = gv_DO_Tasks.Rows.
WhereDoubleParses(Function(i) i.Cells(6).Text.Replace("$", "")).
Sum()

calling a method from another class in vb.net

i have an external class(colorCode.vb) in same project as my main form.vb.
My objective is to send a value as argument as i call a method in the colorCode.vb class, and use the value returned by the method. I don't know if its logically possible . Here i tried this but failed.
in my colorCode.vb Class i have this codes:
Public Sub getValue(ByVal itemCode As Integer)
Dim codeVal() As Integer = {9999, 3034, 3040, 3035}
Dim colorVal As String
For counter As Integer = 0 To codeVal.Count Step 1
If (itemCode = codeVal(counter)) Then
Select Case codeVal(counter)
Case 9999
colorVal = "BRILLIANT WHITE EMULSION"
Case 3034
colorVal = "OFF-WHITE EMULSION"
End Select
End If
Next
End Sub
and in my main form.vb i did this
Private Sub descTextBox_TextChanged(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles descTextBox.TextChanged
Dim colorDesc As colorCode = New colorCode()
Dim itemCode As Integer = Integer.Parse(itemCodeTextBox.Text)
descTextBox.Text = colorDesc.getValue(itemCode)'this line triggers an error.
End Sub
please i need some help here. already running nuts
Please turn on Option Strict. This is a 2 part process. First for the current project - In Solution Explorer double click My Project. Choose Compile on the left. In the Option Strict drop-down select ON. Second for future projects - Go to the Tools Menu -> Options -> Projects and Solutions -> VB Defaults. In the Option Strict drop-down select ON. This will save you from bugs at runtime.
Subs in vb.net do not return values. You need a Function. Instead of looping through the array you can just check if the array contains itemCode and if it does proceed with the Select Case.
Do not put your code in the text changed. It will run every time a character is entered before your user has a chance to enter the entire code. Create a button and use that. Use .TryParse to validate the input. It will return True if valid and fill in the second parameter with the number.
Public Class colorCode
Public Function getValue(ByVal itemCode As Integer) As String
Dim codeVal() As Integer = {9999, 3034, 3040, 3035}
Dim colorVal As String = ""
If codeVal.Contains(itemCode) Then
Select Case itemCode
Case 9999
colorVal = "BRILLIANT WHITE EMULSION"
Case 3034
colorVal = "OFF-WHITE EMULSION"
Case 3040
colorVal = "Blue"
Case 3035
colorVal = "Green"
End Select
Else
colorVal = "No matching color"
End If
Return colorVal
End Function
End Class
And in the form...
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim itemCode As Integer
If Integer.TryParse(TextBox1.Text, itemCode) Then
Dim colorDesc As colorCode = New colorCode()
TextBox2.Text = colorDesc.getValue(itemCode)
Else
MessageBox.Show("Please enter a valid number")
End If
End Sub
Add the return statement. You should also check the value of itemCodeTextBox.Text to make sure it's a numeric value before you call colorDesc.getValue otherwise your program may crash if a non-numeric value is entered into the textbox
You are missing the "return" part of your method.
Public Function getValue(ByVal itemCode As Integer) As String
That's what it'll take to declare you're returning a string value, and then you actually return your vale at the end of the method.
Next
return colorVal
End Sub
There are other ways to do this as well as better ways to deal with your "getValue" code, but this should get you running for now. The rest of the improvements are likely a different Question, or even series of Questions. Or you could go to the Code Review stack and get more help there.

Visual Basic Validation

I'm new to Visual Basics, and I tried implementing input validation for the Mark input in my application where the input can't be empty or < 0 or > 100 or isnumeric(Mark) = False or else an error message would be displayed. However, I tried leaving the input as empty or entering alphabetical values but it returned me a run time error instead. Could anyone assist me in some explanations as well as how to overcome this? Apologies if this is a dumb question.
Note: The data type for the variable "Mark" is set as single. Thank you for your time!
Sub InputMark()
4: For I = 0 To 3
Console.WriteLine("Please enter test marks for test " & I + 1)
Mark(I) = Console.ReadLine
While Mark(I) = Nothing Or Mark(I) < 0 Or Mark(I) > 100 Or IsNumeric(Mark(I)) = False
Console.WriteLine("Please enter valid test marks.")
GoTo 4
End While
Next
End Sub
A couple of things:
Turn option strict on: https://github.com/dday9/vb.net-tutorial/blob/master/Section%201/Chapter%201.md
Using Single.TryParse on the result of ReadLine.
ABANDON GOTO'! In this case a simple Do loop will suffice.
Take a look at this example:
Option Strict On
Option Explicit On
Imports System
Public Module Module1
Public Sub Main()
' Declare an array of Single values
Dim marks(3) As Single
' Loop from 0 to n (in this case 3)
For counter As Integer = 0 To marks.Length - 1
' Declare a variable to check if the conversion was succesful
Dim success As Boolean = False
' Start a Do/Until loop
Do
' Propt the user to enter something
Console.WriteLine("Please enter test marks for test " & counter + 1)
' Check if the conversion from ReadLine to a Single is valid
success = Single.TryParse(Console.ReadLine(), marks(counter)) AndAlso marks(counter) >= 0 AndAlso marks(counter) <= 100
' If not then scold the user
If (Not success) Then
Console.WriteLine("Please enter valid test marks.")
End If
Loop Until success
Next
' Debug - just print out the values of marks
For Each mark As Single In marks
Console.WriteLine(mark)
Next
End Sub
End Module
Live Demo: https://dotnetfiddle.net/guoAzP
The data type for the variable "Mark" is set as single.
One of the nice things about strongly-typed platforms like .Net is most of your validation rules are already enforced. You will not be able to assign a non-number value to Mark, and as a value-type it can't even be null (VB.Net's Nothing will evaluate to 0 for a Single type).
So what we need to do instead is back up, and find where the data is received from the user, before it's assigned to the Mark variable. Somewhere you'll have code like Mark(I) = Console.Readline(), or similar. That's where your testing needs to be. Check the string value received from the user before assigning it to the Single value. And when you come to that point, the best way to accomplish this testing is with the Single.TryParse() function. Then, you can verify the parsed Single value is within the correct range.
Okay, with more complete code we can start to give you some real improvements.
One suggestion is to think in terms of returning a value. It's better design to let this function return the array, rather than communicating through a global variable. I mention that here because it will also mean changing how you call this function.
That out of the way:
Function InputMarks() As Single()
Dim result(3) As Single
For I As Integer = 0 To 3
Dim ErrorMsg As String = ""
Do
Console.Write($"{ErrorMsg}Please enter test marks for test {I+1}: ")
Dim input As String = Console.ReadLine()
ErrorMsg = $"{vbCrLf}Please enter valid test marks.{vbCrLf}{vbCrLf}"
While Not Single.TryParse(input, result(I)) OrElse result(I) < 0 OrElse result(I) > 100
Next
Return result
End Function
No GOTO required or wanted.
If you haven't seen them yet, the $"strings" are using string interpolation and the OrElse operator instead of Or is to get short-circuiting behavior. Modern VB.Net should use OrElse and AndAlso instead of Or and And pretty much all the time.
In this case, using OrElse means we're sure Single.TryParse() completed successfully and we will have a valid number before attempting to verify the range. If the parse operation failed, the range tests won't even be attempted.
I'm going to break this into smaller functions.
First, here is a function to process a string, which returns either a number if it satisfies your conditions, or Single.NaN.
Private Function validateMarkOrNaN(input As String) As Single
Return If(Single.TryParse(input, validateMarkOrNaN) AndAlso 0 <= validateMarkOrNaN AndAlso validateMarkOrNaN <= 100, validateMarkOrNaN, Single.NaN)
End Function
Here is a second function which calls the first when it needs to process the user input. This function handles the flow of a single input. It can be called any number of times, requiring that you pass in the index only so it can be typed in the console.
Private Function getMarkInput(index As Integer) As Single
Dim result As Single
Do
Console.WriteLine($"Please enter test marks for test {index + 1}")
result = validateMarkOrNaN(Console.ReadLine())
If Single.IsNaN(result) Then Console.WriteLine("Please enter valid test marks.")
Loop While Single.IsNaN(result)
Return result
End Function
Your main program dictates the overall flow. Here you can declare how many marks will be read, and loop over your array, getting each input into each element.
Sub Main()
Dim numberOfMarks = 4
Dim Mark(numberOfMarks - 1) As Single
For i = 0 To numberOfMarks - 1
Mark(i) = getMarkInput(i)
Next
End Sub
Similar to your code, this will loop indefinitely as long as the user doesn't enter a valid float.

how can i put check which can take first letter and rest number in textbox

I have one vb.net code where the ID text box is checking if order ID is numeric only. Now my boss want me to change this with first letter and rest numbers like(A0000000) so he needs both ways full numeric and first letter and numeric.
my existing code is.
ElseIf (Not IsNumeric(txtworkorderID.Text)) Then
invalidWorkOrderNumber = True
how can i change this to check if all are numeric or alpha numeric?
I have little bit of programming knowledge.Can someone please help me?
You could use a function like if this is what you mean? Sorry bit trouble understanding what you fully want.
Function CheckForAlphaCharacters(ByVal StringToCheck As String)
If Not Char.IsLetter(StringToCheck.Chars(0)) Then
Return False 'first character is not a letter
End If
'other check if number
For i = 1 To StringToCheck.Length - 1
If Char.IsLetter(StringToCheck.Chars(0)) Then
Return False 'characters contain a letter
End If
next
Return True 'Return true if first character is a letter and rest number
End Function
Basically this will look at the string check to see if the first character is a letter, if it isn't return false, it will then check every character after the first to make sure its not a letter.
If Regex.IsMatch(number, "^[0-9 ]+$") Then
'This will see if the whole string is all numbers! so maybe mix both this and the on above to confirm either ways acceptable?
End If
You could do this with String.Substring to pull out each part and test it both ways. For instance, something like this:
Public Function ParseId(id As String) As ParsedId
Dim number As Integer
If id?.Length > 0 Then
If Integer.TryParse(id, number) Then
Return New ParsedId() With {
.Number = number }
End If
End If
If id?.Length > 1 Then
If Char.IsLetter(id(0)) And Integer.TryParse(id.Substring(1), number) Then
Return New ParsedId() With {
.Letter = id(0),
.Number = number }
End If
End If
Return Nothing
End Function
Public Class ParsedId
Public Property Letter As String
Public Property Number As Integer
End Class
But, if there's any chance that you are going to need to make it even more flexible in the future, you may want to consider using regex (which is shorter anyway):
Public Function ParseId(id As String) As ParsedId
Dim m As Match = Regex.Match(id, "^(?<Letter>[A-Z])?(?<Number>\d+)$")
If m.Success Then
Return New ParsedId With {
.Letter = m.Groups("Letter").Value,
.Number = Integer.Parse(m.Groups("Number").Value) }
Else
Return Nothing
End If
End Function

input validation in vb 2013 input box string conversion error

My program crashes when I enter letter characters into the input box or leave the input box blank. Why is my validation if statement not working?
Option Strict On
Public Class frmPickUpSticks
Dim playersTurn As Boolean = False
Dim remainingSticks As Integer 'count sticks
Dim losses As Integer = 0 'count player losses
Private Sub btnNewGame_Click(sender As Object, e As EventArgs) Handles btnNewGame.Click
lblOutput.Text = ""
remainingSticks = CInt(InputBox("How many matchsticks would you like (5 - 25)?", "Pick Number of Matches!"))
'Validate input
If IsNumeric(remainingSticks) Then
If (remainingSticks >= 5) And (remainingSticks <= 25) Then
DisplayMatches()
If (remainingSticks Mod 4 = 1) Then
MessageBox.Show("You go first!")
playersTurn = True
turns()
Else
MessageBox.Show("I go first.")
turns()
End If
Else
MessageBox.Show("Please enter a number between 5 and 25.")
End If
Else
MessageBox.Show("Input must be numeric.", "Input Error")
End If
You can't automatically take what your user types in an InputBox and pass this input to any function or method that expects a number for input. The InputBox method has been designed to return a string and this string need to be converted but you need to use methods that know how to parse a string. Otherwise methods that are not designed to handle nonnumeric values (CInt) will cause exceptions.
Instead you should try some kind of parsing and the NET Library offers many tools to use. In your case the correct one is Int32.TryParse
Dim remainingSticks As Integer
Dim userInput = InputBox("How many matchsticks .....")
If Int32.TryParse(userInput, remainingSticks) Then
.... ok your remainingStick contains the converted value
Else
MessageBox.Show("You should type a valid integer number between 5-25")
The Int32.TryParse will look at your input string and try to convert to a valid integer value. If it succeed then the second parameter contains the converted integer and returns True, if it fails it return false and your second parameter will have the default value of zero.
Of course after a succesfull conversion to an integer your don't need anymore a check with IsNumeric
You should use a string variable in the inputbox
dim st as string
st = InputBox("How many matchsticks would you like (5 - 25)?", "Pick Number of Matches!"))
remainingSticks = val(st)
. . .