How to Print Multiline VB Text Box Using Windows API Functions

ID Number: Q80867

1.00

WINDOWS

Summary:

Printing the Text property of a multiline text box while maintaining

the line structure requires attention to word wrapping and carriage

return/line feeds. The programmer can either track the number of

characters and lines in code or use Windows API functions to

manipulate the Text property. This article demonstrates those

techniques in a Visual Basic example.

This information applies to Microsoft Visual Basic programming system

version 1.0 for Windows.

More Information:

The example below demonstrates the use of the API function SendMessage

to track the number of lines in a multiline text box and to select and

print the lines in the way they appear--with line breaks or word

wrapping intact. This code will work without modification even if the

form and controls are resized at run time. The actual position of word

wrapping will change.

For more information about API functions relating to text boxes, query

on the following words in the Microsoft Knowledge Base:

API text box manipulate

To build the example:

1. Create a form and place on it a label, a text box, and a command

button.

2. Set the following properties at design time:

Control Property Setting

------- -------- -------

Text box TabIndex 0 (zero, or first in tab order)

Text box MultiLine True

Label AutoSize True

Label CtlName aGetLineCount

3. Add the following code to the Global module:

Declare Function GetFocus% Lib "user" ()

Declare Function SendMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%,

ByVal wParam%, ByVal lParam As Any)

' The above Declare must be placed on one line.

Global Buffer As String

Global resizing As Integer

Global Const EM_GETLINE = &H400 + 20

Global Const EM_GETLINECOUNT = &H400 + 10

Global Const MAX_CHAR_PER_LINE = 80 ' Scale this to size of text box

4. Add the following code to the Form_Load and Form_Resize

procedures:

Sub Form_Load ()

' Size form relative to screen dimensions.

' Could define all in move command but recursive definition causes

' extra paints.

form1.width = screen.width * .8

form1.height = screen.height * .6

form1.Move screen.width\2-form1.width\2, screen.height\2-form1.height\2

End Sub

Sub Form_Resize ()

resizing = -1 ' Global flag for fGetLineCount function call

' Dynamically scale and position the controls in the form.

' This code also is executed on first show of form.

Text1.Move 0, 0, form1.width, form1.height \ 2

Text1.SelStart = Text1.SelStart ' To avoid UAE -see Q80669

command1.Move form1.width\2-command1.width\2, form1.height-form1.height\4

aGetLineCount.Move form1.width \ 2 - command1.width \ 2, Text1.height

X% = fGetLineCount() ' Update to reflect change in text box size

resizing = 0

End Sub

5. Add the following code to the Command1_Click event:

Sub Command1_Click ()

'* Pop up an inputbox$ to allow user to specify which line

'* in the text box to print or print all lines.

'* Also check bounds so that a valid line number is printed

OK = 0 ' Zero the Do Loop flag

NL$ = Chr$(13) + Chr$(10)

prompt$ = "Which line would you like to print?"

prompt1$ = prompt$ + NL$ + "Enter -1 for all"

prompt2$ = "Too many lines" + NL$ + "Try again!" + NL$ + prompt1$

prompt$ = prompt1$

Do

response$ = InputBox$(prompt$, "Printing", "-1")

If response$ = "" Then Exit Sub ' if user hits cancel then exit

If Val(response$) > fGetLineCount&() Then

prompt$ = prompt2$

Else

OK = -1 ' Line chosen is in valid range so exit DO

End If

Loop Until OK

If Val(response$) = -1 Then ' Print all lines

ndx& = fGetLineCount&()

For N& = 1 To ndx&

Buffer = fGetLine(N& - 1)

printer.Print Buffer ' or print to the screen

Next N&

Else ' Print a line

Buffer = fGetLine(Val(response$) - 1)

printer.Print Buffer ' or print to the screen

End If

End Sub

6. Add the following code to the general Declarations section of the

form's code:

Function fGetLine$ (LineNumber As Long)

' This function fills the buffer with a line of text

' specified by LineNumber from the text box control.

' The first line starts at zero.

byteLo% = MAX_CHAR_PER_LINE And (255) '[changed 5/15/92]

byteHi% = Int(MAX_CHAR_PER_LINE / 256) '[changed 5/15/92]

Buffer$ = chr$(byteLo%) + chr$(byteHi%)+Space$(MAX_CHAR_PER_LINE-2)

' [Above line changed 5/15/92 to correct problem.]

text1.SetFocus 'Set focus for API function GetFocus to return handle

x% = SendMessage(GetFocus(), EM_GETLINE, LineNumber, Buffer)

fGetLine$ = Buffer

End Function

Function fGetLineCount& ()

' This function will return the number of lines

' currently in the text box control.

' Setfocus method illegal while in resize event

' so use global flag to see if called from there

' (or use setfocus prior to this function call in general case).

If Not resizing Then

Text1.SetFocus ' Set focus for following function GetFocus

resizing = 0

End If

lcount% = SendMessage(GetFocus(), EM_GETLINECOUNT, 0&, 0&)

aGetLineCount.caption = "GetLineCount = " + Str$(lcount%)

fGetLineCount& = lcount%

End Function

7. Add the following code to the Text1_Change event:

Sub Text1_Change ()

X% = fGetLineCount() '* Update label to reflect current line

End Sub

8. Save the project, then run the application.

9. Enter text into the text box and either let it wrap or use the

ENTER key to arrange lines.

10. Choose the button or TAB and press ENTER.

11. Choose the default (which prints all lines) or enter the line

desired. If you choose Cancel, nothing will print.

12. Resize the form and repeat steps 9 to 11 above. The text will

appear on the printed page as you saw it in the text box. Modify

the example to print to the screen, write to a file, and so forth.

Reference(s):

"Microsoft Windows Programmer's Reference Book and Online Resource"

(Visual Basic Add-on kit number 1-55615-413-5)

Additional reference words: 1.00