VBGood网站全文搜索 Google

首页 - 经验之谈 - 扩展TextBox控件
发表评论(0)作者:不详, 平台:VB6.0+Win98, 阅读:10624, 日期:2001-04-10
Extending the TextBox Control

By Joacim Andersson


As we all know the strength of VB is it注释:s simplicity. Just draw some controls onto a form set some properties during design time, write some "glue code" and you注释:re done (well, almost anyway). VB also provides us with ready to go controls that are the same as Windows own controls (i.e. TextBox, ListBox, CommandButton and so on) or are they?

Windows own controls have more properties then those available in VB. Properties that Microsoft was too lazy to include or just didn注释:t think a VB developer needed.

Take an ordinary multiline textbox for example. It is almost the same as a Windows edit box except for the lack of several properties. An edit box has a property which the VB developer can use to retrieve the number of lines of text it contains. You are also able to get the index of the first visible line (almost like the TopIndex property of a list box) for example.

But is there a way to harness the power of these hidden properties? Luckily there is! With the help of the SendMessage API function you can extend an ordinary textbox so you can treat the lines of text as an array of strings. This is very useful when you want to parse the text. Let注释:s say that you want to create an HTML editor and you want to add color coding (just like the editor in VB itself). Wouldn注释:t it be very useful then if you could extract just the line of text the text caret is on and examines that text for the HTML tags you want to add colors to? Of course it would! But how do you add colors to a textbox you ask? Well, actually you can注释:t. But the code I注释:m going to show you could as easily be used on a rich text box as well as a standard textbox.

Let注释:s get to work...

Now, let注释:s go to work, first you have to declare the SendMessage API function:

Public Declare Function SendMessage _
Lib "user32" Alias "SendMessageA" ( _
ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) As LongYou also need to declare the messages you want to send to the textbox. Let注释:s say you want to write a wrapper function that can tell you the index of the first visible line in the textbox. Then you have to declare the following message:

Public Const EM_GETFIRSTVISIBLELINE = &HCENow you can go ahead and write the function:

Public Function TopLineIndex(txtBox As TextBox) As Long
TopLineIndex = SendMessage(txtBox.hWnd, _
End FunctionThe EM_GETFIRSTVISIBLELINE message doesn注释:t take any arguments so wParam and lParam must be set to 0 (zero). If you want to use this function on a rich text box instead of the standard textbox then just change the argument type to RichTextBox instead of TextBox in the TopLineIndex function. Remember that the line index is zeroing based i.e. the function returns 0 for the first line, 1 for the second and so on.

A rich text box has a method called GetLineFromChar which returns the line index of the given character indexes. The standard textbox lack such a method though. Let注释:s fix that! You need to use the EM_LINEFROMCHAR message witch is declared in the following manner:

Public Const EM_LINEFROMCHAR = &HC9The EM_LINEFROMCHAR message takes the index of the character position in the wParam argument.

Public Function GetLineFromChar(txtBox As TextBox, CharPos As Long) As Long
GetLineFromChar = SendMessage( _
txtBox.hWnd, EM_LINEFROMCHAR, CharPos, 0&)
End FunctionSo if you want to know what line the text caret is on in a multiline textbox you could call the function in this manner:

Dim lngLineIndex As Long
lngLineIndex = GetLineFromChar(Text1, Text1.SelStart)
MsgBox "You are on line number " & lngLineIndex + 1Again remember that the line index is zero based.

You can also do the opposite, finding the index of the first character of a line, with the EM_LINEINDEX message.

Public Const EM_LINEINDEX = &HBB

Public Function GetCharFromLine(txtBox As TextBox, LineIndex As Long) As Long
GetCharFromLine = SendMessage( _
txtBox.hWnd, EM_LINEINDEX, LineIndex, 0&)
End FunctionNow that we know how to find out what line we currently standing on wouldn注释:t it be nice to find out how many lined of text there is in the textbox? Well that注释:s easily done if you know of the EM_GETLINECOUNT message. This message doesn注释:t take any arguments so we must pass 0 to wParam and lParam.


Public Function LineCount(txtBox As TextBox) As Long
LineCount = SendMessage( _
TxtBox.hWnd, EM_GETLINECOUNT, 0&, 0&)
End FunctionOK that was simple enough. Let us go on to the next message: EM_LINELENGTH. This message takes a character index in the wParam argument and returns the length of the line which contains that character:

Public Const EM_LINELENGTH = &HC1

Public Function LineLen(txtBox As TextBox, CharPos As Long) As Long
LineLen = SendMessage( _
TxtBox.hWnd, EM_LINELEENGTH, CharPos, 0&)
End Function

Too easy? Try this...

Easy wasn注释:t it? What did you say? - Too easy? Well, let注释:s do something a little more complicated then? Let us write a function that extracts a single line of text from the textbox. The message we need to use is called EM_GETLINE and it takes the line index in the wParam argument and a buffer string to save the text line in as the lParam argument.

Although this sounds faily easy, the problem is that the message requires that the max length of the string to return be saved in the first word of the buffer. A word is a 16-bit value. How can we save such a value in a VB unicode string? The answer is we can注释:t. So what注释:s the solution then? Well, we have to use a byte array and then convert it to a string afterward.

Public Const EM_GETLINE = &HC4

Public Function GetLine(txtBox As TextBox, LineIndex As Long) As String
Dim bBuffer( ) As Byte  注释:the byte array
Dim lngLength As Long 注释:the max length of the line
Dim sRetVal As String   注释:the text to return

注释:check to see if the LineIndex value is valid
If LineIndex >= LineCount(txtBox)
注释:call the LineCount function shown above
Exit Function 注释:bale out
End If

注释:get the length of the line
lngLength = LineLen(txtBox, GetCharFromLine(txtBox, LineIndex))
注释:check that there is any text on the line
If lngLength < 1 Then
Exit Function
End If

注释:ReDim the byte array
ReDim bBuffer(lngLength)

注释:Save the length in the first word of the array
bBuffer(0) = lngLength And 255
bBuffer(1) = lngLength  256

注释:Send the message
SendMessage txtBox.hWnd, EM_GETLINE, LineIndex, bBuffer(0)

注释:Finally convert the byte array into a string and return it
sRetVal = Left$(StrConv(bBuffer, vbUnicode), lngLength)
GetLine = sRetVal
End FunctionAs you can see there is a lot you can do to extend the functionality in the standard VB controls. And these are just a few of the messages you can use on a textbox. Some of the others are:

EM_CANUNDO - Determines if you can undo the last editing or not.

EM_UNDO - Undo the last editing.

EM_GETMODIFY - Determines if the text in the textbox has been modified. Great to check if you make an editor and want to know if the text have to be saved or not.

EM_SETMODIFY - Manually set the modify flag to true or false. This flag is automatically set to true when the text changes.

I have written a class called cTextBoxEx, with an accompanied demo application, which use all of the mentioned messages and also add a function to delete a single line from the textbox. Furthermore it adds a couple of shortcuts as well. These are CTRL+A to select all and CTRL+Y to cut the current line and put it on the clipboard.