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.

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.

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 If

4.    If KeyAscii = 32 Then
5.        lblWord1Disp.Caption = CInt(lblWord1Disp.Caption) + 1
      End If

6.    If KeyAscii = 46 Then
7.       lblWord1Disp.Caption = CInt(lblWord1Disp.Caption) + 1
          lblSent1Disp.Caption = CInt(lblSent1Disp.Caption) + 1
      End If

End 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.

  1. 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.
  2. 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.
  3. If the User did press the Backspace key, this code will decrement (subtract 1) from the character counter.
  4. To count Words, this If statement checks if the Spacebar was pressed. If KeyAscii is worth 32 the User pressed the Spacebar.
  5. Here you increment the Word counter label exactly like the Character counter above.
  6. To count Sentences, this If statement checks if a period (.) was typed. If KeyAscii is worth 46 the User typed a Period.
  7. 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

  1. You need these variables to count of the number of Characters, Words, and Sentences.
  2. 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.
  3. 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.
  4. 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.
  5. 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 Integer

     CommonDialog1.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 Sub

Save 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.