Beginning Visual Basic - Project 11
Counting Key Strokes
The 11th Project
This will be a modification (enhancement) of the last project.
Begin by loading Project 10 (Choose Open Project under the File menu) and press the F4 key to view the form’s properties, if the Properties window is not already displayed.
- Change the Caption property to Project 11
- Change the form’s Name property from frmProj10 to frmProj11
Open the Properties dialog by selecting Project10 Properties under the Project drop-down menu. Make sure the General tab on the Properties dialog is selected. In the Startup Object combobox make sure frmProj11 is selected (it should be by default). In the Project Name textbox type Project11. In the Project Description textbox type the following: Count the characters, words, and sentences while typing and opening TXT files. Leave all other settings at their defaults and click the OK button.
- Pull down the File menu and choose Save frmPrj10.frm As. Save the form as: frmPrj11.frm.
- Pull down the File menu again and choose Save Project As. Save the project as proj11.vbp.
You will add counters under both the txtEnter and txtView Textboxes. The counters will keep track of the Number of Characters, the Number of Words, and the Number of Sentences. The final result will look something like this:
The labels for the counters, and the counters themselves are Label controls. Use the following Properties Tables while adding the 12 Label controls needed:
Properties (These Labels go below the txtEnter textbox)
Object | Property | Setting |
Label | Name | Label1 (Label) |
Caption | Number of Characters | |
Label | Name | lblChar1Disp (Counter) |
Caption | 0 (zero) | |
Label | Name | Label2 (Label) |
Caption | Number of Words: | |
Label | Name | lblWord1Disp (Counter) |
Caption | 0 | |
Label | Name | Label3 (Label) |
Caption | Number of Sentences: | |
Label | Name | lblSent1Disp (Counter) |
Caption | 0 |
Properties (These Labels go below the txtView textbox)
Object | Property | Setting |
Label | Name | Label4 (Label) |
Caption | Number of Characters | |
Label | Name | lblChar2Disp (Counter) |
Caption | 0 (zero) | |
Label | Name | Label5 (Label) |
Caption | Number of Words: | |
Label | Name | lblWord2Disp (Counter) |
Caption | 0 | |
Label | Name | Label6 (Label) |
Caption | Number of Sentences: | |
Label | Name | lblSent2Disp (Counter) |
Caption | 0 |
In order to keep track of the number of Characters, Words, and Sentences that are typed as the User is typing them into the txtEnter textbox, you must examine each key they press. Textboxes come with a KeyPress event procedure. Below is the code you must type into the txtEnter_KeyPress event procedure. The lines are numbered and explained below (do not type the line numbers or the Private Sub or End Sub lines):
Private Sub txtEnter_KeyPress(KeyAscii As Integer)
1. If KeyAscii <> 8 Then
2. lblChar1Disp.Caption = CInt(lblChar1Disp.Caption) + 1
Else
3. lblChar1Disp.Caption = CInt(lblChar1Disp.Caption) - 1
End If4. If KeyAscii = 32 Then
5. lblWord1Disp.Caption = CInt(lblWord1Disp.Caption) + 1
End If6. If KeyAscii = 46 Then
7. lblWord1Disp.Caption = CInt(lblWord1Disp.Caption) + 1
lblSent1Disp.Caption = CInt(lblSent1Disp.Caption) + 1
End IfEnd Sub
The line that begins Private Sub is first line of the KeyPress event procedure (don’t type it!). It’s shown here so that you can see how the ASCII value of the key that the User pressed is stored in the KeyAscii variable that is passed to this procedure by the Operating System as an integer.
- If KeyAscii has a value of 8 it means the User pressed the Backspace key. This test makes sure they didn’t press the Backspace key before you increment the character counter.
- By converting the Caption property of the lblChar1Disp label into an integer with the CInt function, you can increment it (add 1) to count another character.
- If the User did press the Backspace key, this code will decrement (subtract 1) from the character counter.
- To count Words, this If statement checks if the Spacebar was pressed. If KeyAscii is worth 32 the User pressed the Spacebar.
- Here you increment the Word counter label exactly like the Character counter above.
- To count Sentences, this If statement checks if a period (.) was typed. If KeyAscii is worth 46 the User typed a Period.
- When the user types a Period, you need to increment your Sentence counter and add 1 to your Word counter as well.
The previous code takes care of counting Characters, Words, and Sentences in the txtEnter textbox while the User is typing. To count the same elements of a text file that you read into the txtView textbox, you will create your own custom procedure. The difference between a custom procedure that you create and an event procedure that comes ready-made with a Control, is that your procedure must be called manually. While Event Procedures are executed when the User interacts with a control on your form, either by clicking on it or passing the mouse over it, etc. You will create a custom procedure and manually call it from within the mnuOpen_Click event procedure. To create your custom procedure do the following:
Position the cursor below the last line of code in the General Declarations section of your code window, above the first Private Sub line.
For the procedure’s name type Private Sub CWS_Counter (CWS stands for Character, Word, Sentence. That’s an underline connecting CWS to Counter).
Press the Enter key. The End Sub line which terminates the procedure is added automatically. Your cursor should be sitting between these two lines:
Private Sub CWS_Counter()
End Sub
Before adding any code, make this change to the first line:
Private Sub CWS_Counter(Str As String)
By adding Str As String into the parentheses you have changed your procedure so that a string must be passed to it when you call it (namely the string that’s to have its Characters, Words, and Sentences counted). Now add the following code to your CWS_Counter procedure. The lines are numbered and explained below:
1. Dim iChars As Integer
Dim iWords As Integer
Dim iSents As Integer
2. iWords = 0
iSents = 0
3. For iChars = 1 To Len(Str)
4. If Mid(Str, iChars, 1) = " " Then iWords = iWords + 1
If Mid(Str, iChars, 1) = "." Then
iWords = iWords + 1
iSents = iSents + 1
End If
Next iChars
5. lblChar2Disp.Caption = iChars
lblWord2Disp.Caption = iWords
lblSent2Disp.Caption = iSents
- You need these variables to count of the number of Characters, Words, and Sentences.
- Here you initialize the Word (iWords) and Sentence (iSents) counters. Notice how you haven’t initialized the Character (iChars) counter. That’s because it’s initialized a couple of lines later when it’s used as the For-Next loop counter.
- You’re going to examine each character of the string that’s passed to the CWS_Counter procedure. This For-Next loop begins with the 1st character and spans the Length of the string which is returned by the Len function.
- The Mid function has 3 parameters:
Mid(String, Starting-Character-Position, Number-of-Characters-to-Return)
So Mid(Str, iChars, 1) will return 1 character from the Str string starting at position iChars in the string (This is how you examine the string, one character at a time). The first If statement tests for a Space (" ") and increments the Word counter accordingly. The second If statement tests for a Period and increments the Word and Sentence counters accordingly. Note: This If statement has a unique structure. You’ll notice that it has no corresponding End If statement to terminate it. That is because the single line of code that is to be executed when the If test is true follows directly after the word Then, on the same line.- Once the For-Next loop is finished, the entire string (Str) has been scanned character by character, so you can assign the counter values for Characters (iChars), Words (iWords), and Sentences (iSents) to their corresponding labels.
Below is code similar to the code you have already typed for the mnuOpen_Click event procedure from the last project, with the addition of the call to your CWS_Counter procedure (Do Not change your current code to match this code, just insert the CWS_Counter line to your code where shown). Notice how sText is being passed as a parameter to the CWS_Counter procedure.
Private Sub mnuOpen_Click()
Dim sFileName As String
Dim sText As String
Dim sLine As String
Dim iLineCount As IntegerCommonDialog1.DialogTitle = "Enter Name Of File To Open"
CommonDialog1.Filter = "Text File (*.txt) | *.txt"
CommonDialog1.InitDir = "A:\"
CommonDialog1.ShowOpen
sFileName = CommonDialog1.filename
CommonDialog1.filename = ""
If sFileName <> "" Then
‘Change the MousePointer to an Hour Glass, since this may take a while
Screen.MousePointer = vbHourGlass
Open sFileName For Input As #1
sText = ""
iLineCount = 0
Do While Not EOF(1) And iLineCount < 50
Line Input #1, sLine
‘Append sLine to sText and add back on the Carriage Return and Line
' Feed that the Line Input ‘command strips off
sText = sText & sLine & Chr(13) & Chr(10)
iLineCount = iLineCount + 1
Loop
Close #1
txtView.Text = sText
‘Pass the contents of the file (sText) to the CWS_Counter procedure so it can
' count the Characters, Words, and Sentences
>>> CWS_Counter sText
‘Change the MousePointer back to an Arrow
Screen.MousePointer = vbDefault
End If
End SubSave and test your program thoroughly.
Required Enhancement
Get the code in txtEnter_KeyPress to work properly. By that I mean to test it thoroughly. Try using the Backspace key and see what your counters do. Are their values accurate? Try entering erroneous values. What happens to the counters? Your goal is to modify the code in the txtEnter_KeyPress event procedure until it works.
Hint: Please don't read this hint section until you've struggled for awhile on your own with this problem.
The Best way to get the real-time Character, Word, and Sentence counting to work in the txtEnter textbox is not to place any code in the txtEnter_KeyPress event procedure at all. But instead to take a totally different approach to the problem. Start by completely deleting your txtEnter_KeyPress event procedure. Now make a copy your CWS_Counter procedure and paste it into your code window right below the General Declarations section. Name this procedure CWS_Counter2 and modify the last three lines of code in it so that this new procedure looks like this:
Private Sub CWS_Counter2(Str As String)
Dim iChars As Integer
Dim iWords As Integer
Dim iSents As Integer
iWords = 0
iSents = 0
For iChars = 1 To Len(Str)
If Mid(Str, iChars, 1) = " " Then iWords = iWords + 1
If Mid(Str, iChars, 1) = "." Then
iWords = iWords + 1
iSents = iSents + 1
End If
Next iChars
lblChar1Disp.Caption = iChars
lblWord1Disp.Caption = iWords
lblSent1Disp.Caption = iSents
End Sub
Then add this line of code to the txtEnter_Change event procedure
CWS_Counter2 txtEnter.Text
Now every time the user presses a key, all the text in the txtEnter textbox is processed. After making these changes, take your program through its paces. You'll still have a couple of small problems to deal with, but you'll find that this solution is far superior to the txtEnter_KeyPress method which is fraught with loop holes. This just goes to show that while a method to deal with a programming problem may seem plausible at first, you can't stop being open to the possibility that there is a better way.