VB 'Option Strict' issue with conversion from double to long - vb.net

I just started using VB and had to switch 'Option Strict' to on. I got this error in my code saying "Option Strict On disallows implicit conversions from 'double to long.'" I know what it means, but my friend and I can not seem to resolve the issue.
Error: "Option Strict disallows implicit conversions from 'Double' to 'Long' for both value1 and value 2 on line 24. (Private Sub btnIntDiv_Click)
Here is the code:
Public Class frmMathCalculator
Dim value1 As Double
Dim value2 As Double
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
value1 = Integer.Parse(txtValue1.Text)
value2 = Integer.Parse(txtValue2.Text)
Dim addResult As Double = value1 + value2
lstAnswer.Items.Add(addResult)
End Sub
Private Sub btnSubtract_Click(sender As Object, e As EventArgs) Handles btnSubtract.Click
Dim subtractResults As Double = value1 - value2
lstAnswer.Items.Add(subtractResults)
End Sub
Private Sub btnMultiply_Click(sender As Object, e As EventArgs) Handles btnMultiply.Click
Dim multiplyResults As Double = value1 * value2
lstAnswer.Items.Add(multiplyResults)
End Sub
Private Sub btnLongDiv_Click(sender As Object, e As EventArgs) Handles btnLongDiv.Click
Dim divideResults As Double = value1 / value2
lstAnswer.Items.Add(divideResults)
End Sub
Private Sub btnIntDiv_Click(sender As Object, e As EventArgs) Handles btnIntDiv.Click
Dim intDivResults As Double = value1 \ value2
lstAnswer.Items.Add(intDivResults)
End Sub
Private Sub btnMod_Click(sender As Object, e As EventArgs) Handles btnMod.Click
Dim modResults As Double = value1 Mod value2
lstAnswer.Items.Add(modResults)
End Sub
Private Sub btnExponent_Click(sender As Object, e As EventArgs) Handles btnExponent.Click
Dim expResults = value1 ^ value2
lstAnswer.Items.Add(expResults)
End Sub
End Class

Consider:
Public Class frmMathCalculator
Private value1 As Double 'we typically specify access levels like public or private for variables declared inside a class
Private value2 As Double
'We need to do this often, so rather than repeat the parsing in every
'Button click handler we create a sub we can call
Private Sub ParseUserInput()
'TODO: switch these to TryParse and provide user feedback if input is bad
'TODO: make this sub a function that returns a Boolean and return false if input is bad
value1 = Double.Parse(txtValue1.Text) 'value1 is a double, so use double.parse not int.parse
value2 = Double.Parse(txtValue2.Text)
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
ParseUserInput() 'set our value variables
'VB will be able to work out that double + double is double so can skip the "As Double" bit
Dim addResult = value1 + value2
'List.Items.Add takes an object, and the list will simply call ToString on it when it wants
' to display it. For a double this is fine unless we want to format it - more on this later
lstAnswer.Items.Add(addResult)
End Sub
I haven't done all of them, but the pattern is repeatable..
This is ultimately an exercise in precision and knowing about datatypes. Functions take variables that are a certain type and emit data that has a type, possibly different. You need to keep consistency and generally be aware all the time what types of data are being put where
Double.Parse takes in a string, returns a double. Double+Double results in a double. ListBox.Items.Add takes in an object (returns nothing, but some collection add methods return something that you might capture and use) so you can put anything you like in but it's important to appreciate that to display it it has to be a string. To achieve this, if you didn't tell List to display the contents of a particular property of the item, then list will just blindly call ToString on the object you put in. For a double this is fine- it just takes the 1.234 double and makes it a string "1.234" which looks the same. If however you have created some custom class like Person, but you haven't provided a specialized variation of ToString that eg returns first name and last name, then all you'll see in your list is "MyNamespace.Person" over and over again. This is because by default ToString just returns the name of the kind of object.
Suppose in this case we wanted to format our doubles to 3dp all the time.. instead of putting the double in the list and having the list do the ToString, we could format a string and put it in: list.Items.Add(aDouble.ToString("0.000"))
So, which to use and why? If you're just using this list to hold the results, then format a string. You could even put the whole sum in: list.Items.Add($"{value1} + {value2} = {result:0.000} (to 3dp)")
If you're going to get these numbers back out of the list and do something with them later, keep them as doubles
If you want to get them out later and also have a customized presentation of them you need to add them as their own custom object that knows the value but can present a customized representation when asked. That's probably a bit advanced for now
With option strict off VB is very(overly) forgiving; if you try and put a string into something that takes a double, it'll try and convert the string to double for you. That's great, perhaps, but it doesn't encourage a strict mindset in the programmer - and eventually it goes wrong. Being mindful about the ins and outs and their types, and being explicit, is a great way to avoid subtle bugs in the future

Related

How to fix System.FormatException: 'Input string was not in a correct format'

I am trying to change Font Family in VB.NET for a RichTextBox
I have tried searching through google and even specific sites for just code problems. Nothing works.
These are the codes for FontFamily changing (it is a tool strip combo button)
Private Sub TscFontFamily_TextChanged(sender As Object, e As EventArgs) Handles tscFontFamily.TextChanged
rtbContent.SelectionFont = New Font(Convert.ToString(tscFontFamily.Text), Convert.ToInt32(tscFontSize.Text))
End Sub
And the FontSize (also a tool strip combo button)
Private Sub tscFontSize_TextChanged(sender As Object, e As EventArgs) Handles tscFontSize.TextChanged
Dim newSize As Single = Convert.ToSingle(tscFontSize.Text)
rtbContent.SelectionFont = New Font(rtbContent.SelectionFont.FontFamily, newSize)
End Sub
I expect this to work since it makes sense, right? But it says "input string was not in correct format" and if i remove the Convert.ToString and other convert code, it says something like "Conversion from string "" to type 'Single' is not valid.".
Also to mentiion, is the fact that i have actually tried Convert.ToString and Convert.ToDouble and basically everything for BOTH.
There is no need to call ToString on tscFontFamily.Text because the Text property of a TextBox returns a String; you're essentially saying return a String value from this String value.
Convert.ToInt32 is a quick and dirty was of converting String values to Int32 (aka integer) values. If you're relying on input from a user then you should almost always use Int32.TryParse instead. This method returns a Boolean value based on if the conversion of the input is successful and then the second parameter passed is a reference variable so if the result of the method is True then the second parameter contains the converted value. However, it looks like you're wanting a Single value in which case you'd actually use Single.TryParse.
Here would be an example applying the changes to your code:
Private Sub tscFontSize_TextChanged(sender As Object, e As EventArgs) Handles tscFontSize.TextChanged
Dim newSize As Single
If Single.TryParse(tscFontSize.Text, newSize) Then
rtbContent.SelectionFont = New Font(rtbContent.SelectionFont.FontFamily, newSize)
End If
End Sub

Basic math functions in VB.net

So i am currently in process of adding a basic calculator that allows the user to define 2 variables and then press a button that puts the variables into a basic math equation and presents the result but i think i have gone about it completely wrong.
this is my first time using math functions in VB and would appreciate it if someone can show me where im going wrong.
this is my code so far:
Imports System.Math
Public Class SOGACALC
Dim soga As String = Math.Abs(72 - months.Text) * opp.Text
Private Sub SOGACALC_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SOGAValue.Text = soga
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
HOME.Show()
Me.Close()
End Sub
End Class
Where you have written
Dim soga As String = Math.Abs(72 - months.Text) * opp.Text
I suspect that you are anticipating that soga will be a function of the properties referred to in that and will change when those properties change. It does not work that way.
The way to get a value which varies depending on its parameters is to define a function, so you might have:
Friend Function Soga(monthValue As Control, oppThing As Control) As String
Dim month As Integer = CInt(monthValue.Text)
Dim opp As Decimal = CDec(oppThing.Text)
Return (Math.Abs(72 - month) * opp).ToString()
End Function
and call it like:
'TODO: Give Button1 a meaningful name.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SOGAValue.Text = Soga(months, opp)
End Sub
where there are controls names "months" and "opp" on the form.
I strongly recommnend that you use Option Strict On - it points out problems in code and suggests corrections for you.
Notice that I used the Decimal type for opp - I had to guess at a suitable type because nowhere in the code you showed us was there any indication of what type it needs to be.
An improvement would be to use TryParse methods instead of CInt/CDec, so that you can inform the user if they have made a simple typing error.

Alternative Process

I have 2 buttons and a DataGridView with 2 Columns (0 & 1).
The 1st button transfers a randomized cell from the Column(1) to a TextBox. Then, it stores that Cell in variable (a), plus the cell that opposites it in variable (b).
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim rnd As New Random
Dim x As Integer = rnd.Next(0, Form1.DataGridView1.Rows.Count)
Dim y As Integer = 1
Dim a As String = Form1.DataGridView1.Rows(x).Cells(y).Value
Dim b As String = Form1.DataGridView1.Rows(x).Cells(y - 1).Value
TextBox3.Text = a
End Sub
The 2nd button, however, is supposed to compare if another TextBox's text has the same string variable (b) has as Strings. Now, if so, then it has to display a certain message and so on...
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If TextBox4.Text = b Then '<<< ISSUE HERE!
MsgBox("Correct! ^_^")
ElseIf TextBox4.Text = "" Then
MsgBox("You have to enter something first! O_o")
Else
MsgBox("Wrong! >,<")
End If
End Sub
The problem is that the variable (b) is surely not shared across the two "private" subs. And so, there is NOTHING to compare to in the 2nd button's sub! I presume that the solution here is to split the "randomization process" into a separate function, then execute it directly when the 1st button gets activated. Furthermore, that function's variables have to be SHARED somehow, and I certainly don't know how!
Thanks for Mr. Olivier, the code has been improved significantly! Yet, I still encounter a "wrong" comparison issue, somehow!
Dim RND As New Random
Dim x As Integer
Private Function GetCell(ByVal rowIndex As Integer, ByVal cellIndex As Integer) As String
Return Form1.DataGridView1.Rows(rowIndex).Cells(cellIndex).Value
End Function
Private Sub btnRoll_Click(sender As Object, e As EventArgs) Handles btnRoll.Click
x = RND.Next(0, Form1.DataGridView1.Rows.Count)
tbxRoll.Text = GetCell(x, 1)
End Sub
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
If tbxSubmit.Text = GetCell(x, 0) Then
MsgBox("Correct! ^_^")
ElseIf tbxSubmit.Text = "" Then
MsgBox("You have to enter something first! O_o")
Else
MsgBox("Wrong! >,<")
End If
End Sub</code>
Well, unbelievably, I read a guide about "comparison operations" in VB.net and tried out the first yet the most primal method to compare equality - which was to use .Equals() command - and worked like a charm! Thank God, everything works just fine now. ^_^
If tbxSubmit.Text.Equals(GetCell(x, 0)) Then
Alright now... This is going to sound weird! But, following Mr. Olivier's advise to investigate "debug" the code, I rapped the string I'm trying to compare with brackets and realized that it's been outputted after a break-line space! So, I used the following function to remove the "white-space" from both of the comparison strings! And it bloody worked! This time for sure, though. ^_^
Function RemoveWhitespace(fullString As String) As String
Return New String(fullString.Where(Function(x) Not Char.IsWhiteSpace(x)).ToArray())
End Function
If RemoveWhitespace(tbxSubmit.Text) = RemoveWhitespace(GetCell(x, 0)) Then
Turn the local variables into class fields.
Dim rnd As New Random
Dim x As Integer
Dim y As Integer
Dim a As String
Dim b As String
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
x = rnd.Next(0, Form1.DataGridView1.Rows.Count)
y = 1
a = Form1.DataGridView1.Rows(x).Cells(y).Value
b = Form1.DataGridView1.Rows(x).Cells(y - 1).Value
TextBox3.Text = a
End Sub
These fields can now be accessed from every Sub, Function and Property.
Of course Button3_Click must be called before Button2_Click because the fields are initialized in the first method. If this is not the case then you should consider another approach.
Create a function for the Cell access
Private Function GetCell(ByVal rowIndex As Integer, ByVal cellIndex As Integer) _
As String
Return Form1.DataGridView1.Rows(rowIndex).Cells(cellIndex).Value
End Function
And then compare
If TextBox4.Text = GetCell(x, y - 1) Then
...
And don't store the values in a and b anymore. If y is always 1 then use the numbers directly.
If TextBox4.Text = GetCell(x, 0) Then
...
One more thing: give speaking names to your buttons in the properties grid before creating the Click event handlers (like e.g. btnRandomize). Then you will get speaking names for those routines as well (e.g. btnRandomize_Click).
See:
- VB.NET Class Examples
- Visual Basic .NET/Classes: Fields

Find and Replace using another form

I have my frmMainwhich has RichTextBox1 and I have a button btnfind&Replacewhich whose click event pops out another minute form frmFindandReplace which has two textboxes: TextBoxSearch and TextBoxReplace with two buttons: replaceButton and findButton. I cannot seem to get my code for instances of finding a word in textbox and an instance of replacing it. Here is my code:
Public Class frmFindandReplace
Dim txtClientArea As RichTextBox
Private Sub TextBoxSearch_TextChanged(sender As Object, e As EventArgs) Handles TextBoxSearch.TextChanged
End Sub
Private Sub frmFindandReplace_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub replaceButton_Click(sender As Object, e As EventArgs) Handles replaceButton.Click
End Sub
Protected Friend Sub findButton_Click(sender As Object, e As EventArgs) Handles findButton.Click
Dim a As String
Dim b As String
a = TextBoxSearch.Text
b = InStr(StartPosition, a, txtClientArea)
If b Then txtClientArea.Focus()
txtClientArea.SelectionStart = b - 1
txtClientArea.SelectionLength = Len(a)
txtClientArea.ScrollToCaret()
End Sub
The findButton code doesnot even work. Throws an error!
Error 3: Overload resolution failed because no accessible 'InStr' can be called with these arguments:
'Public Function InStr(Start As Integer, String1 As String, String2 As String, [Compare As Microsoft.VisualBasic.CompareMethod = Microsoft.VisualBasic.CompareMethod.Binary]) As Integer': Value of type 'System.Windows.Forms.TextBox' cannot be converted to 'String'.
'Public Function InStr(String1 As String, String2 As String, [Compare As Microsoft.VisualBasic.CompareMethod = Microsoft.VisualBasic.CompareMethod.Binary]) As Integer': Value of type 'System.Windows.Forms.RichTextBox' cannot be converted to 'Microsoft.VisualBasic.CompareMethod'. C:\Users\Joseph GodwinKE\Documents\Visual Studio 2013\Projects\simpleapp\frmFindandReplace.VB 25 13 Simple app
I know I have not done much but am new and all my efforts of searching a solution over the internet have failed! Thank you I hope someone will help me pls.
A few pointers:
InStr returns an integer.
Check the documentation as it'll show you have the search values the wrong way around.
Turn Option Explicit on to help find your issues.
This should work better.
Private Sub findButton_Click(sender As Object, e As EventArgs) Handles findButton.Click
Dim searchString As String
Dim findPos As Integer
Try
searchString = TextBoxSearch.Text
findPos = InStr(txtClientArea.Text, searchString)
If findPos > 0 Then txtClientArea.Focus()
txtClientArea.SelectionStart = findPos - 1
txtClientArea.SelectionLength = searchString.Length
txtClientArea.ScrollToCaret()
Catch ex As Exception
MessageBox.Show(String.Concat("An error occurred: ", ex.Message))
End Try
End Sub
If you want your code work you need to pass the reference to the RichTextBox present in the first form to the findandReplace form.
Otherwise you will not be able to work with that instance of the RichTextBox.
Usually, this means that when you create and open an instance of the findandReplace form you pass the reference to the RichTextBox to work with in the call to the constructor. Something like this
Dim fReplace As frmFindandReplace = New frmFindandReplace(Me.txtClientArea)
fReplace.Show()
Here the New call reaches the constructor of frmfindandReplace. This call is usually hidden by VB.NET but you could add it writing explicit code for it
Public Class frmFindandReplace
Dim txtClientArea As RichTextBox
Public Sub New (ByVal txt as RichTextBox)
txtClientArea = txt
End Sub
Now the global variable txtClientArea inside the findandReplace class is assigned to the existing reference of the RichTextBox present in the first form and you could happily work with it
Protected Friend Sub findButton_Click(sender As Object, e As EventArgs) Handles findButton.Click
Dim a As String
a = TextBoxSearch.Text
Dim position = txtClientArea.Find(a, 0, RichTextBoxFinds.MatchCase)
.....
End Sub
And please make yourself a favor and start using the more complete methods available from the NET Framework library and stop using the old fashioned VBA methods.
For example the RichTextBox has a method that does exactly what you are trying to do in code. Find, search the content of the textbox and if it founds a match it highlight the text and return the starting position of the text.
There is no replace builtin method but having the position and the length is really simple to implement your own replacing code.
You Have defined b as a string. Change it to an integer. Also Instr doesn't allow you to set a start position, just a string to search and the string to search for and optionally the type of search - Binary or Text.
Finally rather than type If b then, use If b>0 then rather than turning off Option Strict. It's always better to write code with Option Strict on as it makes you write better code and in the long run is easier to chase down errors

Case Statement not working with String Literals

Hi all I am trying to learn VB and am having trouble with some code I am using. I would like my program to output a specific number based on if a check box is checked using case statements but my code is not working.
Public Class frmBTPW
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles btncalc.Click
Dim dblhdr As Double
Dim dblfdr As Double
Dim dbltdr As Double
dblhdr = 24
dblfdr = 35
dbltdr = 50
Select Case "Power Wash Rental"
Case "Half Day Rental"
If chkhd.Checked = True Then
txtrc.Text = "poop"
End If
Case "Full Day Rental"
If chkFD.Checked = True Then
txtrc.Text = dblfdr
End If
End Select
End Sub
Private Function Button1_Click() As CheckBox
Throw New NotImplementedException
End Function
End Class
Help would be greatly appreciated.My code isn't outputting anything in the text-box.
Beyond case statements, respectfully I think you should read up on the distinction between a literal value and a variable. "Power Wash Rental" is nothing more than a series of characters, AKA a string: (In this case "P" followed by "o" etc.) Likewise, "Half Day Rental" is a series of characters, "H" followed by "a" etc.)
"Power Wash Rental" is a literal string. So is ""Half Day Rental" and of course they will never match.
Whereas:
Dim A as string
A = TextBox1.text
Now, A is a variable. It is a string which contains whatever series of characters (text) is typed into the textbox.
This is a simple way to do it.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
chkhd.tag = 24 ' store values in the check boxes
chkfd.tag = 35 ' using the tag property
chktd.tag = 50 ' and later add up the values
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles btncalc.Click
dim total as double = 0
total += IF(chkhd.checked, cdbl(chkhd.tag), 0)
total += IF(chkfd.checked, cdbl(chkfd.tag), 0)
total += IF(chktd.checked, cdbl(chktd.tag), 0)
msgbox(total)
End Sub
However, I think you might want radio buttons instead of checkboxes.
Checkboxes can all be checked. Radio buttons can only have one at a time.
This solution allows you to keep your price with the checkbox -- you could do this in the form designer instead of form load.
I would recommend reading up on Case Statements. Currently you will never get anywhere as your using a string to what, nothing. You also do not need a case for this... Also if the first condition is true and the last one is as well, the last one win's for setting the text, didn't know if you had this there for a reason or not?
If chkhd.Checked = True Then
txtrc.Text = "poop"
End If
If chkFD.Checked = True Then
txtrc.Text = dblfdr
End If
As others have stated your Case statement isn't working because you are using string literals to compare "Power Wash Rental" to "Half Day Rental" which will always be false. Plutonix was also correct in saying that a ComboBox for the rental duration should be used. The only reason not to be is if you were calculating cumulative rental days/amounts; however in that situation you should be using some sort of NumericUpDown for your multiplier against a time duration.
Here is an example that should help you get started. You could make the structure into a type of keyed collection or make it a wrapper class for a dictionary object which would make be easier to use in code. The following may not be exactly plug-and-play with your project, however it should help give you some ideas on how to handle the situation.
Option Strict On
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.ComboBox1.Items.AddRange({PowerWashRentals.halfDayText, PowerWashRentals.FullDayText, PowerWashRentals.TwoDayText})
AddHandler ComboBox1.SelectedValueChanged, AddressOf Me.ComboBox1_SelectedChanged
End Sub
Private Sub ComboBox1_SelectedChanged(sender As Object, e As EventArgs)
Dim cBox As ComboBox = DirectCast(sender, ComboBox)
Select Case cBox.SelectedItem.ToString
Case PowerWashRentals.halfDayText
Label1.Text = PowerWashRentals.HalfDayPrice.ToString
Case PowerWashRentals.FullDayText
Label1.Text = PowerWashRentals.FullDayPrice.ToString
Case PowerWashRentals.TwoDayText
Label1.Text = PowerWashRentals.TwoDayPrice.ToString
End Select
End Sub
End Class
Public Structure PowerWashRentals
Public Const HalfDayPrice As Double = 24
Public Const FullDayPrice As Double = 35
Public Const TwoDayPrice As Double = 50
Public Const halfDayText As String = "Half Day Rental"
Public Const FullDayText As String = "Full Day Rental"
Public Const TwoDayText As String = "Two Day Rental"
End Structure