Using vb.net Count all occurrences of vbCrLf in TextBox - vb.net

I have a TextBox that I am trying to count all the occurrences of vbCrLf.
The counting is working, the issue is every time a vbCrLf is issued I would like to subtract 33 from some Integer.
The code as written now only subtracts the number of vbCrLf, NOT the number + 33.
Question is how to subtract 33 every time the Enter Key is pressed and a vbCrLf is issued ?
I have posted Updated Code The Question Was Answered and Issues SOLVED
I have also added additional code that enhances the management of the TextBox
You will need these Imports
Imports System.IO
Imports System.Text
Public Class frmThree
Dim path As String = "C:/Users/Me/source/repos/TestForms/TestForms/Resource/"
Dim total As Integer
Dim vbLfTotal As Integer
Dim chrPermited As Integer = 333 'chrPermited
Private Sub btnBack_Click(sender As Object, e As EventArgs) Handles btnBack.Click
Me.Close()
frmOne.Show()
End Sub
Private Sub tbHaveOne_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tbHaveOne.TextChanged
Dim spaceCount, letterCount, carReturn As Integer
spaceCount = 0
letterCount = 0
carReturn = 0
Dim str As String = tbHaveOne.Text
For Each chr As Char In str
If chr = vbLf Then
carReturn += 1
ElseIf Char.IsLetterOrDigit(chr) Then
letterCount += 1
ElseIf Char.IsWhiteSpace(chr) Then
spaceCount += 1
End If
Next
vbLfTotal = carReturn * 29
total = chrPermited - letterCount - spaceCount - vbLfTotal
tbHaveTwo.ForeColor = Color.Black
tbHaveTwo.Text = total & " More Character Can Be Entered"
While total < 10
tbHaveTwo.Clear()
tbHaveTwo.ForeColor = Color.Red
tbHaveTwo.Text = "Only " & total & " More Character Can Be Entered"
Exit While
End While
If total = 5 Then
PlaySystemSound()
End If
If total < 1 Then
tbHaveTwo.Clear()
tbHaveTwo.Text = "No More Data Entry" & total
Call ansQ()
End If
End Sub
Sub PlaySystemSound()
My.Computer.Audio.PlaySystemSound(
System.Media.SystemSounds.Hand)
End Sub
Public Sub ansQ()
Const Msg As String = "YES Save Data" + vbCrLf + vbNewLine + "NO CANCEL"
Const Title As String = "Save or Exit"
Const Style = vbYesNo + vbQuestion + vbDefaultButton2
Dim result = MsgBox(Msg, Style, Title)
If result = vbYes Then
Call writeDATA()
ElseIf result = vbNo Then
'tbHaveOne.ReadOnly = True
'tbHaveOne.Enabled = False
tbHaveTwo.Text = "NO Was Selected"
'result = 0
End If
End Sub
Public Shared Sub tbHaveOne_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles tbHaveOne.KeyDown
'NOTE this Sub is Shared It seems to run all the time like a listener
If e.KeyCode = Keys.Enter And frmThree.total < 40 Then
e.SuppressKeyPress = True
End If
End Sub

The TextBox.Lines.Count returns the count of the lines separated by newline characters (ControlChars.NewLine or Environment.NewLine when you hit the Enter key.) and not the count of the word-wrapped lines in a multiline TextBox. If you set the WordWrap property to False you will see the difference. See the TextBoxBase.Lines remarks section.
You could call the SendMessage method and the EM_GETLINECOUNT message if you need to get the count of the lines regardless whether they are word-wrapped or separated new lines:
Imports System.Runtime.InteropServices
Private Const EM_GETLINECOUNT As UInteger = &HBA
<DllImport("user32.dll")>
Private Shared Function SendMessage(ByVal hWnd As IntPtr,
ByVal msg As Integer,
ByVal wp As IntPtr,
ByVal lp As IntPtr) As IntPtr
End Function
Private Sub PrintCounts()
Dim c = tbHaveOne.Text.
Aggregate(New With {
.WhiteSpaces = 0, .Lf = 0, .NewLines = 0, .Lines = 0, .Others = 0
},
Function(x, y)
x.Lf += If(y = ControlChars.Cr, 1, 0)
x.WhiteSpaces += If(Char.IsSeparator(y), 1, 0)
x.Others += If(Not Char.IsWhiteSpace(y), 1, 0)
Return x
End Function)
c.NewLines = tbHaveOne.Lines.Count
c.Lines = SendMessage(tbHaveOne.Handle, EM_GETLINECOUNT,
IntPtr.Zero, IntPtr.Zero).ToInt32
tbHaveTwo.Text = $"WhiteSpaces: {c.WhiteSpaces} Lf: {c.Lf} " +
$"New Lines: {c.NewLines} Lines {c.Lines} Others: {c.Others}"
End Sub
Private Sub tbHaveOne_TextChanged(sender As Object,
e As EventArgs) Handles tbHaveOne.TextChanged
PrintCounts()
End Sub
Please note:
The Char.IsSeparator method is used to get the count of the white spaces since the Char.IsWhiteSpace method will return True when evaluating; vbCr, vbLf, vbCrLf, vbTab, ...etc. Alternatively, you can combine the Char.IsWhiteSpace and the Char.IsControl methods to evaluate the white spaces. The Remarks section has more.
The Others is the count of everything else including the letters, digits, punctuations, symbols...etc. You may wont to examine the other Char.IsXXX methods of the Char structure.
Check out the values of the Lines and NewLines fields as you type.

You can't check if the current character is vbcrlf because CRLF is two characters. Just check for the LF, this way if you ever use the logic on a file prepared by a unix system, it still works (unix has LF, Windows has CRLF)
Private Sub tbHaveOne_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tbHaveOne.TextChanged
Dim spaceCount = 0
Dim letterCount = 0
Dim lineCount = 0
For Each c As Char In tbHaveOne.Text
If c = vbLf Then
lineCount += 1
ElseIf Char.IsLetterOrDigit(c) Then
letterCount += 1
ElseIf Char.IsWhiteSpace(c) Then
spaceCount += 1
End If
Next c
someInteger -= (lineCount * 33)
The System.Char has static methods that check characters are in certain Unicode ranges - for your letter count you were counting anything that wasn't a space, including carriage returns, punctuation and other things that aren't letters, as letters. You're free to carry on doing that if you want but it'll maybe lead to a bit of a confusing count if you triple count the newlines
The counting is working the issue is every time a vbCrLf is issued I would like to subtract 33 from some Integer
The code as written now only subtracts the number of vbCrLf NOT the number + 33
I didn't quite get this. You say you want to subtract 33 every time there is a new line, which is what I've done. The second sentence reads like the requirement is to subtract the (number of new lines + 33) from someInteger. If this is what you want to do, change the * on the last line to +
Question is how to subtract 33 every time the Enter Key is pressed and a vbCrLf is issued ?
This is a different thing entirely to "count all occurrences of"; it isn't to do with counting the lines in a textbox, and you can't do it from such code. Counting the number of times the user has ever pressed enter in a textbox needs a hook to the event raised when a user pressed a key while the box had focus. Add a handler for the KeyPress or KeyDown event:
Private Sub tbHaveOne_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles tbHaveOne.KeyPress 'KeyEventArgs for KeyDown event
If e.KeyChar = vbCr Then 'use e.KeyCode = Keys.Enter in KeyDown event
someInteger -= 33
End If
End Sub

We have a lot of great Discussion and Fixed Answers on this question
One of the questions in the discussion was how to handle punctuation and special characters
We would like to offer this code as a way to manage both punctuation and special characters
Dim spaceCount, letterCount, carReturn, punCount, syCount As Integer
spaceCount = 0
letterCount = 0
carReturn = 0
punCount = 0
syCount = 0
Dim str As String = tbHaveOne.Text
For Each chr As Char In str
If chr = vbLf Then
carReturn += 1
ElseIf Char.IsLetterOrDigit(chr) Then
letterCount += 1
ElseIf Char.IsWhiteSpace(chr) Then
spaceCount += 1
ElseIf Char.IsPunctuation(chr) Then
punCount += 1
ElseIf Char.IsSymbol(chr) Then
syCount += 1
End If
Next
vbLfTotal = carReturn * 29
total = chrPermited - letterCount - spaceCount - vbLfTotal - punCount - syCount
tbHaveTwo.ForeColor = Color.Black
tbHaveTwo.Text = total & " More Character Can Be Entered"

Related

Validation Class(dll)

İ m tring to do a validation dll for may textbox. my dll's codes like this
Public Class validatedll
Function onlynumbers(ByVal r As String)
Dim list As String, character As String
Dim i As Integer
list = "1234567890"
i = 1
If r <> "" Then
cntinue:
If i > 10 Then
MsgBox("invalid character. Last character will be delete !")
r = Mid(r, 1, Len(r) - 1)
Return r
Exit Function
End If
character = Mid(list, i, 1)
If Microsoft.VisualBasic.Right(r, 1) = character Then Exit Function
i = i + 1
GoTo cntinue
Else
Exit Function
End If
End Function
End Class
And i m useing this dll in my project like this
Imports ModifiedTextbox
Public Class Form1
Public validate As New validatedll
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
' TextBox1.Text = validate.onlynumbers(TextBox1.Text)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TextBox1.Text = validate.onlynumbers(TextBox1.Text)
End Sub
End Class
My problem is, if i use this dll in TextBox1_TextChanged event, Textbox1's text showing to me nothing because dll is not returning numbers. İf i use this code with button, code is working and deleting last character if a letter.
How can i use this dll in textchanged event? pls help to me.
My best regards
The main reason your weren't getting a return is whenever your loop found the right character it exited.
A few things:
A function should always have an As clause to specify the return type. You may remember the return today what about a year from now?
Never use GoTo. VB.net has several functional loops that will do pretty whatever you like.
Assuming that you want to extend this to support other validations you might want to consider another approach:
Public Class validatedll
Sub onlynumbers(tb As TextBox)
While Not tb.Text = "" AndAlso Not Double.TryParse(tb.Text, vbNull)
tb.Text = tb.Text.Remove(tb.Text.Length - 1)
tb.SelectionStart = tb.Text.Length
End While
End Sub
End Class
I made it a sub that gets passed a reference to the textbox. This keeps the mechanism of your validation more compartmentalized. This will also make it easier if you decide to extend this class to be a custom control.
This tests if the string is a number, and keeps subtracting characters until it's empty or is a number. This will work for on the fly validation or on exit validation.
i resolve my problem. Maybe somebody needs that. You can add which chars to wanna allow with orelse;
Function onlynumbers(ByVal chars As String, ByVal exclmtn As Boolean, Optional title As String = "Title what you want")
If chars = "" Then Exit Function
If _
Microsoft.VisualBasic.Right(chars, 1) = "1" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "2" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "3" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "4" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "5" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "6" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "7" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "8" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "9" OrElse _
Microsoft.VisualBasic.Right(chars, 1) = "0" Then
Return chars
Exit Function
Else
If exclmtn = False Then
chars = Mid(chars, 1, Len(chars) - 1)
Return chars
Exit Function
End If
If exclmtn = True Then
MsgBox("invalid. last char ll delete !", , title)
chars = Mid(chars, 1, Len(chars) - 1)
Return chars
Exit Function
End If
End If
End Function

label loop for next visual basic

Currently my code looks like this:
Private Sub btnMotivate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMotivate.Click
Dim intNumber As Integer
Dim strStudy As String = "Study!"
intNumber = InputBox("How many times do you want to be motivated? Please use numbers!")
If intNumber < 1 Then
MessageBox.Show("Please use a number from 1 - 10!")
End If
If intNumber > 10 Then
MessageBox.Show("Please use a number from 1 - 10!")
End If
> For intCounter = 1 To intNumber Step 1
lblMotivation.Text = strStudy
> Next
End Sub
As you can see, I have added a variable and the loop runs fine, but I need help phrasing the code so whatever number the user inputs, is the number of lines of "Study!" displayed. I have tried using the string but also using the label with Environment.NewLine hoping it would add a new line every time the loop runs
You are setting the label text to strStudy each time. This doesn't add to what's there already, it just replaces it.
You need to add strStudy to the text that already exists, something like this:
' start with an empty string
lblMotivation.Text = ""
For intCounter = 1 To intNumber Step 1
' append the study string and a newline
lblMotivation.Text = lblMotivation.Text & strStudy & Environment.NewLine
Next
Instead of using a TextBox, use a ListBox to display the word Study! n -times. It is also good practice not to instruct the user to do something, but prevent it in code. Also note how intNumber has been changed to *inp*Number As Object
Private Sub btnMotivate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMotivate.Click
Dim inpNumber As Object
Dim strStudy As String = "Study!"
Dim intCounter As Integer
inpNumber = InputBox("How many times do you want to be motivated?")
If Not IsNumeric(inpNumber) Then
MessageBox.Show("Please enter a number!")
ElseIf inpNumber < 1 OrElse inpNumber > 10 Then
MessageBox.Show("Please enter a number from 1 - 10!")
Else
ListBox1.Items.Clear()
For intCounter = 1 To inpNumber Step 1
ListBox1.Items.Add(strStudy)
Next
End If
End Sub

VB.net basic error with String <-> Char Conversion

I was programming a little game to pratice myself and i went on an error I weren't able to fix :S. This is the start of an hanging game. (Dont know if its the correct name in english :) ) I need to take a word from a file, 1 per line, and let the player guess the word with a limited numbers of try.
I think my error is related to string/char comparaison and manipulation or, what i write in the text label. I tryed to find some tutorial or problem already solved on internet but nothing was really the same as this ... :(
I change the type of variable many time, read the debugger line per line but I never finded what was wrong .. :S If you are good with VB, plz help me fix that :O (you can also give your comment/improve)
Thx, TheFlame
Code:
Imports System.IO
Public Class Pendu
Public Structure StructMot
Public MotSecret() As Char
Public LettreDecouverte() As Char
End Structure
Dim Mot As StructMot
Dim i As Integer = 0
Private Sub ButtonA_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonA.Click, ButtonB.Click,
ButtonC.Click, ButtonD.Click, ButtonE.Click, ButtonF.Click, ButtonG.Click, ButtonH.Click, ButtonI.Click, ButtonJ.Click,
ButtonK.Click, ButtonL.Click, ButtonM.Click, ButtonN.Click, ButtonO.Click, ButtonP.Click, ButtonQ.Click, ButtonR.Click,
ButtonS.Click, ButtonT.Click, ButtonU.Click, ButtonV.Click, ButtonW.Click, ButtonX.Click, ButtonY.Click, ButtonZ.Click
i = i + 1
ActiveControl.Visible = False
PictureBox1.Image = ImageList1.Images(i - 1)
Dim j As Integer = 0
For j = 0 To Mot.MotSecret.Length - 1
If ActiveControl.Text = Mot.MotSecret(j) Then
Mot.LettreDecouverte(j) = Mot.MotSecret(j)
End If
Next j
Label1.Text = ""
For j = 0 To Mot.MotSecret.Length - 1
Label1.Text = Label1.Text + " "
If Mot.LettreDecouverte(j).Equals("") Then
Label1.Text = Label1.Text + "_"
Else
Label1.Text = Label1.Text + Mot.LettreDecouverte(j)
End If
Next j
End Sub
Private Sub JouerToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles JouerToolStripMenuItem.Click
GenereMot()
End Sub
Private Sub Pendu_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
GenereMot()
End Sub
Function GenereMot()
Dim NbItems As Integer
Dim Aleatoire As New Random()
Dim NbAleatoire As Integer
ListBox1.Items.AddRange(System.IO.File.ReadAllLines("listemot.txt"))
NbItems = ListBox1.Items.Count
NbAleatoire = Aleatoire.Next(NbItems)
Mot.MotSecret = ListBox1.Items(NbAleatoire)
Return Mot
End Function
End Class
the Mot.LettreDecouverte can be Nothing i.e. not initialized as you don't set any value for it.
This can cause error to happen both in Mot.LettreDecouverte(j) = Mot.MotSecret(j) line and also in If Mot.LettreDecouverte(j).Equals("") Then... line of your code.
change your ButtonA_Click event handler code as following:
i = i + 1
ActiveControl.Visible = False
PictureBox1.Image = ImageList1.Images(i - 1)
Dim j As Integer = 0
If Mot.LettreDecouverte Is Nothing OrElse Mot.LettreDecouverte.Length < Mot.MotSecret.Length Then
Mot.LettreDecouverte = Space(Mot.MotSecret.Length) '* initiate it by enough number of space chars
End If
For j = 0 To Mot.MotSecret.Length - 1
If ActiveControl.Text = Mot.MotSecret(j) Then
Mot.LettreDecouverte(j) = Mot.MotSecret(j)
End If
Next j
Label1.Text = ""
For j = 0 To Mot.MotSecret.Length - 1
Label1.Text = Label1.Text + " "
If Mot.LettreDecouverte(j).Equals(" "c) Then '*Note: i use space char, not empty string
Label1.Text = Label1.Text + "_"
Else
Label1.Text = Label1.Text + Mot.LettreDecouverte(j)
End If
Next j
Note that the value of Mot.LettreDecouverte(j) is always a char and a char can not be an empty string as you typed in Mot.LettreDeciouverte(j).Equals(""). i initiate the LettreDeciouverte with an array of space chars such that we can check its elements comparing against " "c char.

Listbox Multiple KeyPress

How can I achieve the search functionality that works with multiple keys in CheckedListBox and ListBox?
Example: I have the following items in the list:
1000
1500
1520
2010
5001
5102
When I press the key 1, instantly search the first hit that match with the first char that starts with '1'.
But, If I want locate the item "5102", the list only will can search the 5, and then, I need go manually to identify the wanted item.
Answering to my own question. This it's a clean solution that doesn't need too much work (All the code can handle in the KeyDown event):
Sub DropDownListBox_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
If Char.IsLetterOrDigit(Chr(e.KeyValue)) Then
e.SuppressKeyPress = True
If TryCast(sender.tag, Timer) Is Nothing Then
Dim vTimer As New Timer
vTimer.Interval = 1000
vTimer.Tag = ""
sender.Tag = vTimer
AddHandler vTimer.Tick,
Sub(Timer As Object, eventsArgs As System.EventArgs)
Timer.Tag = ""
If Not TryCast(sender, CheckedListBox).Visible Then
Timer.dispose() : Timer = Nothing
End If
End Sub
Else
sender.Tag.Stop() : sender.Tag.Start()
sender.Tag.Tag &= Chr(e.KeyValue)
Dim vIndex As Integer = TryCast(sender, CheckedListBox).FindString(sender.Tag.Tag)
If vIndex <> -1 Then TryCast(sender, CheckedListBox).SelectedItem = TryCast(sender, CheckedListBox).Items(vIndex)
End If
End If
End Sub
Basically I use the TAG object to keep a Timer running every 1 sec; then, if the user typing several chars, the process will find exactly the wanted text.
Any comment or feedback will be welcome.
Public Class Form1
Dim Type As String
Private Sub ListBox1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles ListBox1.KeyPress
If Asc(e.KeyChar) <> 8 Then
If Asc(e.KeyChar) < 65 Or Asc(e.KeyChar) > 122 Then
e.Handled = True
' Clear String at Escape key press & Reset Listbox location
If Asc(e.KeyChar) = 27 Then
Type = ""
ListBox1.SelectedIndex = -1
ListBox1.SelectedItem = ListBox1.SelectedIndex
End If
End If
If Asc(e.KeyChar) = 27 Then
' Do not add escape key char to string at key press
Else
' add char one by one after key press
Type &= e.KeyChar
Dim TL As Integer = Type.Length
Dim i As Integer = 0
For i = 0 To ListBox1.Items.Count - 1
' match key press in total items in listbox
'MsgBox(Format$(Mid$(ListBox1.Items(i), 1, TL)))
If Format$(Mid$(ListBox1.Items(i), 1, TL)) = Type Then
ListBox1.SelectedIndex = i
End If
Next
End If
End If
End Sub
End Class

VB.net timer tick event question

I wrote the following code to build a basic game that makes two cars race (randomgenerator) and let the user pick their bets on the end results. I've gone terribly wrong somewhere in that my cars only race halfway then get stuck behind the start line and the programme keeps repeating the beeping and the messsagebox.show and adding results to the 'incorrect' box. I've spent the last three days trying to figure out where I've gone wrong and I'm just missing it. Thank you for taking the time to look at this. I really appreciate any advice.
The only way to terminate the application is to hit 'stop debugging'.
Option Explicit On
Option Strict On
Public Class MainForm
Private Sub exitButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles exitButton.Click
Me.Close()
End Sub
Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'fills the list box with items, then selects the first item
resultListBox.Items.Add("No guess")
resultListBox.Items.Add("Tie")
resultListBox.Items.Add("Red Car wins")
resultListBox.Items.Add("White car wins")
resultListBox.SelectedIndex = 0
End Sub
Private Sub startButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles startButton.Click
raceTimer.Enabled = True
End Sub
Private Sub raceTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles raceTimer.Tick
' moves two cars from a starting line to a finish line on the form
' displays a message indicating the race results
' calculates and displays the number of times the user selects the
' correct race result from the resultlistbox, and the number
' of times the user selects an incorrect race result
Dim randomgenerator As New Random
Dim whitenewlocation As Integer
Dim rednewlocation As Integer
Dim raceresult As String
Dim userchoice As String
Dim finishline As Integer = finishTextBox.Left + 1
Static numbercorrect As Integer
Static numberincorrect As Integer
' save the user's list box selection, then disable the list box
userchoice = resultListBox.SelectedItem.ToString
resultListBox.Enabled = False
'calculate the new location of each picture box's right border
' don't allow the right border to go beyond the finish line
whitenewlocation = whitePictureBox.Right + randomgenerator.Next(0, 11)
If whitenewlocation > finishline Then
whitenewlocation = finishline
End If
rednewlocation = redPictureBox.Right + randomgenerator.Next(0, 11)
If rednewlocation > finishline Then
rednewlocation = finishline
End If
'move each picture box toward the finish line
whitePictureBox.SetBounds(whitenewlocation - whitePictureBox.Width, 0, 0, 0, BoundsSpecified.X)
redPictureBox.SetBounds(rednewlocation - redPictureBox.Width, 0, 0, 0, BoundsSpecified.X)
'the following selection structure is processed only when at least
' one of the picture boxes is at the finish line
If whitePictureBox.Right = finishline _
OrElse redPictureBox.Right = finishline Then
'disable the timer
raceTimer.Enabled = False
End If
'sound a beep to indicate the end of the race
For x As Integer = 1 To 5
Console.Beep(100, 100)
Next x
'store the result of the race in a variable
If whitenewlocation = rednewlocation Then
raceresult = "Tie"
ElseIf whitenewlocation > rednewlocation Then
raceresult = "White car wins"
Else
raceresult = "red car wins"
End If
'display the race results
MessageBox.Show("Race Over!" & ControlChars.NewLine & raceresult, _
"Car Race", MessageBoxButtons.OK, MessageBoxIcon.Information)
'move the picture boxes back to the starting line
whitePictureBox.SetBounds(12, 0, 0, 0, BoundsSpecified.X)
redPictureBox.SetBounds(12, 0, 0, 0, BoundsSpecified.X)
'if the user did not want to guess the race results, then
'don't update or display the counter values; otherwise,
'compare the race results to the user's selection and update
' the appropriate counter, then display both counter values
If userchoice <> "no guess" Then
If raceresult = userchoice Then
numbercorrect = numbercorrect + 1
Else
numberincorrect = numberincorrect + 1
End If
correctLabel.Text = Convert.ToString(numbercorrect)
incorrectLabel.Text = Convert.ToString(numberincorrect)
End If
'enable the list box
resultListBox.Enabled = True
End Sub
End Class
Your timer is doing everything at every tick. The code that handle the end of the race is processed every tick of the timer. It should be enclosed in the if-logic that handles the end of the race.
Allmost eveything after the comment "sound a beep to indicate the end of the race" should be inside the check for end of race:
If whitePictureBox.Right = finishline OrElse redPictureBox.Right = finishline Then
'put all logic of the "ending" of the race here
end if
So this code will work as you expected:
Private Sub raceTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RaceTimer.Tick
' moves two cars from a starting line to a finish line on the form
' displays a message indicating the race results
' calculates and displays the number of times the user selects the
' correct race result from the resultlistbox, and the number
' of times the user selects an incorrect race result
Dim randomgenerator As New Random
Dim whitenewlocation As Integer
Dim rednewlocation As Integer
Dim raceresult As String
Dim userchoice As String
Dim finishline As Integer = finishTextBox.Left + 1
Static numbercorrect As Integer
Static numberincorrect As Integer
' save the user's list box selection, then disable the list box
userchoice = resultListBox.SelectedItem.ToString
resultListBox.Enabled = False
'calculate the new location of each picture box's right border
' don't allow the right border to go beyond the finish line
whitenewlocation = whitePictureBox.Right + randomgenerator.Next(0, 11)
If whitenewlocation > finishline Then
whitenewlocation = finishline
End If
rednewlocation = redPictureBox.Right + randomgenerator.Next(0, 11)
If rednewlocation > finishline Then
rednewlocation = finishline
End If
'move each picture box toward the finish line
whitePictureBox.SetBounds(whitenewlocation - whitePictureBox.Width, 0, 0, 0, BoundsSpecified.X)
redPictureBox.SetBounds(rednewlocation - redPictureBox.Width, 0, 0, 0, BoundsSpecified.X)
'the following selection structure is processed only when at least
' one of the picture boxes is at the finish line
If whitePictureBox.Right = finishline _
OrElse redPictureBox.Right = finishline Then
'disable the timer
RaceTimer.Enabled = False
'sound a beep to indicate the end of the race
For x As Integer = 1 To 5
Console.Beep(100, 100)
Next x
'store the result of the race in a variable
If whitenewlocation = rednewlocation Then
raceresult = "Tie"
ElseIf whitenewlocation > rednewlocation Then
raceresult = "White car wins"
Else
raceresult = "red car wins"
End If
'display the race results
MessageBox.Show("Race Over!" & ControlChars.NewLine & raceresult, _
"Car Race", MessageBoxButtons.OK, MessageBoxIcon.Information)
'move the picture boxes back to the starting line
whitepicturebox.SetBounds(12, 0, 0, 0, BoundsSpecified.X)
redpicturebox.SetBounds(12, 0, 0, 0, BoundsSpecified.X)
'if the user did not want to guess the race results, then
'don't update or display the counter values; otherwise,
'compare the race results to the user's selection and update
' the appropriate counter, then display both counter values
If userchoice <> "no guess" Then
If raceresult = userchoice Then
numbercorrect = numbercorrect + 1
Else
numberincorrect = numberincorrect + 1
End If
correctlabel.Text = Convert.ToString(numbercorrect)
incorrectlabel.Text = Convert.ToString(numberincorrect)
End If
'enable the list box
Resultlistbox.Enabled = True
End If
End Sub