Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How does the VB IDE do it?? 2

Status
Not open for further replies.

elziko

Programmer
Nov 7, 2000
486
GB
I have a rich text box in which I need to colour certain strings with certain colours. Much like the way the VB IDE colours VB keywords etc.

I've done this by just stepping through all the text in the text box, checking for certain strings and once found using [color].SelText[/color] and [color].SelLen[/color] to color that chunk of text.

However, this takes a long time and needs the rich text box to be visibly scrolled through.

In the VB IDE it happens instantly when you copy un formatted text into the code window. How does it do it so efficiently? Would I have to do the formatting inside a string in memory and maybe add rich text formatting information? Or is there an easier way?

Cheers

elziko
 
You may want to consider creating your own ActiveX control that would do this automatically. Otherwise, as inefficient as it may sound, I think that you've done the best thing. Neil Konitzer, President
Freisoft
 
Hi,
I'm also very interested in this topic, but.....
wooow, creating an ActiveX control with all the functionality of an rtf-textbox and additional syntax-highlighting.
Can you / or somebody else give some hints or links how to do that?

And by the way: HTML-Editors like Homesite or Phase5 also support Syntax-highlighting. - How do they do it?

Thanx,
bateman23
 
You can go a LOT faster than you currently are.

Firstly, scrolling through the Rich Text Box causes a massive overhead. So you need to figure out some way of preventing this. eg, preventing the window from updating, which you can do with the following procedures:
[tt]
Option Explicit

Private Declare Function LockWindowUpdate Lib "user32" _
(ByVal hWnd As Long) As Long

Public Sub LockWindow(hWnd As Long)
LockWindowUpdate hWnd
End Sub

Public Sub UnlockWindow()
LockWindowUpdate 0
End Sub
[/tt]
Call the LockWindow function before doing all your searching and replacing, and then call UnloackWindow to return to normal operation. You should find this speeds things up a fair amount.

You may also find that you can optimise your code a little.

Nevertheless, serially searching through large chunks of test using VB is inherently a 'slow' operation. ANd I stated that you can go a lot faster. So, a slighty different approach is called for. And one that immediately springs to mind is to use Regular Expresions, courtesy of Microsoft VBScript.

Here are the requirements for this example to work:
You need 2 Rich text Boxes on your form. The first should contain your source text, as it currently does (presumably)The second can have it's Visible property set to False. Add a reference to the Microsoft VBScript Regular Expressions library. Oh, and drop in a command button for good measure. Then play with the following:

Private Sub ColorWord(strWord As String, lColor As Long)
Dim re As RegExp

RichTextBox2.Text = strWord
RichTextBox2.SelStart = 0
RichTextBox2.SelLength = Len(strWord)
RichTextBox2.SelColor = lColor

Set re = New RegExp

re.Global = True
re.IgnoreCase = True
're.
re.Pattern = strWord
RichTextBox1.TextRTF = re.Replace(RichTextBox1.TextRTF, RichTextBox2.SelRTF)
End Sub
 
Strongm, thanks for your suggestions.

I've tried locking my form and that does indeed get rid of the look of it updating as you watch which is very inefficient.

I'll try the other method for actually coloring the words soon too. And I may look into trying to do it using RTF formatting within a string and bypass the RTF box altogether.

Bateman23,

As far as a control is concerned it would be fairly easy to create once the above is done but it would be a bit time consuming to make all the properies and events and methods of the underlying control visible to the final ActiveX control.

If I do suceed in making it as fast as the VB IDE I may well create a control.... who knows??

However in the meantime I could dig up some links on how to make user controls if you wanted to give it a go. It really isn't very difficult. Just time consuming.

Cheers

elziko
 
Question: Do you need to colorize this only when you load the RTB, or during typing into it and when letting the user cut & paste?
 
elziko - try the Regular Expression suggestion as soon as possible; I think you'll be pleasantly surprised.
 
dilettante,

I intend to have the rich text box colourise on load, after cut and paste, after typing and when the cursor is moved from one line to another it will re-check the first line. Much as the VB IDE does. However I'll be consentrating on just having a single function that'll colorise the entire contents efficiently first.

strongm,

I'll give it a go now!
 
Well I'm afraid I haven't had the results I expected!:-(

I had two arrays, each containing words that need to be of a certain colour. Each one had around 110 words in it. I used your code and created the followinf function:

Public Sub ColorizeWords(rtf As RichTextBox)

Dim re As RegExp
Dim lColour As Long
Dim strWord As String
Dim i
Dim tim As Long

tim = GetTickCount

LockWindow frmMain.hWnd

Set re = New RegExp
re.Global = True
re.IgnoreCase = True

For i = LBound(arrBlackKeywords) To UBound(arrBlackKeywords)
frmMain.RichTextBox2.Text = arrBlackKeywords(i)
frmMain.RichTextBox2.SelStart = 0
frmMain.RichTextBox2.SelLength = Len(arrBlackKeywords(i))
frmMain.RichTextBox2.SelColor = vbBlack
re.Pattern = arrBlackKeywords(i)
rtf.TextRTF = re.Replace(rtf.TextRTF, frmMain.RichTextBox2.SelRTF)
Next i
For i = LBound(arrBlueKeyWords) To UBound(arrBlueKeyWords)
frmMain.RichTextBox2.Text = arrBlueKeyWords(i)
frmMain.RichTextBox2.SelStart = 0
frmMain.RichTextBox2.SelLength = Len(arrBlueKeyWords(i))
frmMain.RichTextBox2.SelColor = vbBlue
re.Pattern = arrBlueKeyWords(i)
rtf.TextRTF = re.Replace(rtf.TextRTF, frmMain.RichTextBox2.SelRTF)
Next i

UnlockWindow

MsgBox GetTickCount - tim
End Sub


For a box filled with tens of lines of text your suggested code was FOUR times SLOWER than my original way of doing it and with hundreds of lines of text your code was TWICE as slow.

Now my code has changed a little from the code you posted (not much though) so its quite possible I've done something a little silly to sap the performance.

Have you got any ideas or comments??

Many thanks,

elziko
 
I decided that if running code on rich text boxes is so slow then I could get rid of the second rich textbox altogether.

I added an extra dimension to my array of words that need to be coloured. With each plain text work I stored the rich text format text that describes how it must be coloured. For example.

arrBlueKeyWords(0,1) = "ABlueWOrd " and

arrBlueKeyWords(1, 1) = "{\rtf1\ansi\ansicpg1252\deff0\deftab720{\fonttbl{\f0\fswiss MS Sans Serif;}{\f1\froman\fcharset2 Symbol;}{\f2\fmodern Courier New;}{\f3\fmodern Courier New;}{\f4\fswiss MS Sans Serif;}{\f5\fswiss\fprq2 Arial;}{\f6\froman\fprq2 Times New Roman;}}{\colortbl\red0\green0\blue255;}\deflang2057\horzdoc{\*\fchars }{\*\lchars }\pard\plain\f2\fs20\cf0 ABlueWord }"

However, when I tested this it took TWICE AS LONG AGAIN!!:-0 X-)

Any further ideas???

elziko
 
How about if you store your text in a variable, and only copy that portion that would be visible to the user to the rich text box? You'd have to manage the scrollbar yourself.

Or maybe store the program text as a collection of "token" objects, and you'd loop through the collection to figure out how to display it. You'd use an IToken interface to ensure you can loop through the collection cleanly.

[tt]
clsIToken:
Public Property Get IToken_Word as string
End Property

clsToken:
Implements IToken
Private m_sWord as String
Public Property Get IToken_Word as string
Word = "{rtfcodes" & m_sWord & "}"
End Property

clsWhitespace:
Implements IToken
Private m_sWhitespace as string
Public Property Get IToken_Word as string
Word = m_sWhitespace
End Property

frmEditWindow:
Private m_colWords as collection

Private Sub ShowWords(ByVal lStartLine as Long, _
ByVal lNumLines as Long, _
ByRef EditControl as RichTextBox)
Dim i as Long
Dim j as Long
Dim lStartPoint as long
Dim o as Object
Dim sTemp as string

' Find starting object
i = 1
for j = 0 to m_colWords.count
set o = m_colWords(j)
if o is clsWhitespace Then
if o.Word = vbcrlf Then
i = i + 1
Endif
If i >= lStartLine Then
Exit For
End If
End If
set o = nothing
Next j
lStartPoint = j

' now, starting at that point, copy the Words into the control

i = 0
sTemp = ""
for j = lStartPoint to m_colWords.Count
set o = m_colWords(j)
sTemp = sTemp & o.Word
if o is clsWhitespace Then
i = i + 1
if i >= lNumLines then
Exit For
Endif
set o = nothing
next j
EditControl.Text = sTemp

' Now set the scroll bar manually

' (left as an exercise for the reader)

End Sub
[/tt]

Hope this helps.

Chip H.
 
The VB IDE doesn't use a rich textbox, so duplicating its speed and efficiency within VB would be rather difficult, perhaps impossible. It was most likely written in Visual C++, so I would recommend writing a component in Visual C++ to do the job.
 
Yes, several ideas and comments...

Basically, the example I provided was illustrative - and constructed to replace multiple instances of a single word. You have then decided to use 200 times...So, sure that'll slow it down somewhat, since we're back to a certain level of serial processing (word after word after word). But if you read up on Regular Expressions, you'll find that you don't need to do it this way. You can do a single search and replace for multiple different words. eg:
[tt]
Public Sub ColorizeWords(rtf As RichTextBox)

Dim strPattern As String
Dim tim As Long

'tim = GetTickCount

'LockWindow frmMain.hWnd

strPattern = BuildPatternFromArray(arrBlackKeywords)
Call ReplacePattern(rtf, strPattern, vbBlack)

strPattern = BuildPatternFromArray(arrBlueKeywords)
Call ReplacePattern(rtf, strPattern, vbBlue)



'UnlockWindow

'MsgBox GetTickCount - tim
End Sub

Public Function BuildPatternFromArray(arrPattern() As String) As String
Dim lp As Long
Dim strPattern As String
For lp = LBound(arrPattern) To UBound(arrPattern)
strPattern = strPattern + arrPattern(lp) + "|"
Next
BuildPatternFromArray = Left(strPattern, Len(strPattern) - 1)
End Function

Public Function ReplacePattern(rtf As RichTextBox, patrn As String, lColor As Long) As String ', replStr) As String
Dim re As RegExp

Set re = New RegExp
re.Global = True
re.Pattern = "(" + patrn + ")"
re.IgnoreCase = True
rtf.TextRTF = re.Replace(rtf.TextRTF, BuildReplacementString("$1", lColor))

End Function

Public Function BuildReplacementString(strString, lColor As Long) As String
frmMain.RichTextBox2.Text = "$1"
frmMain.RichTextBox2.SelStart = 0
frmMain.RichTextBox2.SelLength = 2
frmMain.RichTextBox2.SelColor = lColor
BuildReplacementString = frmMain.RichTextBox2.SelRTF
End Function
[/tt]
Using the above code I can replace every single word in a 10k piece of text using two source word lists of 200 words each in under 2 seconds.
 
strongm,

Sorry, I think I must have missed the point of Regular Expressions!!

I've had another go and its now a little quicker than the original method (about twice as fast depending on an=mount of text).

So thanks a lot.

chiph,

Although there has been a big improvement its still nowhere as fast as the VB IDE. I refuse to believe that its because its written in VB instead of C. Maybe your suggestion will solve the problem. If I have time to try it out I'll let you know.

Cheers

elziko
 
It is VERY easy to miss the point of Regular Expressions. Also, they are sometimes a bit of overkill. But there can also be times where they are really useful.

As an aside, the code I provided to you can probably be optimised a little bit further by doing both all the pattern building stuff up front rather than within the ColorizeWords functions.
 
Hi,

I tried several tips, mentioned in this thread, but there still are a few
- or some more questions.

1) referring to strongm's first solution
I called the LockWindow Function - and it works fine, until the user changes
the active Window (or switches to another app). Then it is
unlocked again and begins scrolling. Is there any way to prevent this ?

2) Regular Expressions
I can't use the reference to the MS VBScript Regular Expressions
library. Despite I've added it, I can't use it. :-( Why ?????
... anyway. there are different opinions: Regular Expressions - are they
fast or are they slow?
.... and: I want to color a certain amount of text between to "separators" e.g.
the text between &quot; and &quot; and between < and >. For example:
<this text is blue &quot;This is green&quot; blue again> and black <blue again>
Is this the right range of use for RegExp ?

3) Chiph: &quot;token&quot; objects
What are these &quot;token objects&quot; and a &quot;IToken Interface&quot;
- searched the web for hours, but couldn't find anything...
Another question to your code:
What do these
- clsIToken:
- clsToken:
- clsWhitespace:
- frmEditWindow:
.. mean? Are these the names of functions, where I have to place the code ?
(I think frmEditWindow is clear, but the rest?)

U see, I'm very unfamiliar with RegExp and &quot;token objects&quot; (?) - Hope you can
bring some light into the dark. ;-)

Thanx,
bateman23
 
>LockWindowUpdate

Sorry, don't know a workaround for this. Only one window can be locked at a time, so if another app does a lock then your lock is lost. It may be worth investigating the ExcludeUpdateRgn API call, although I haven't tried this one myself.

>I can't use it. Why ?????
Not enough info to hazard a suggesting a solution. Can you tell us more?

>anyway. there are different opinions: Regular Expressions -are they fast or are they slow?

No, there aren't really any different opinions on this as such. The speed issue raised here related to how the Regular Expressions were actually used. When used properly (and that's the tricky bit!) it would be difficult, if not impossible, to write a pure VB solution that could keep up. That said, there are occassions where the overhead of Regular Expressions make them an inappropriate solution.

><this text is blue &quot;This is green&quot; blue again> and black <blue again>

Matching for wildcard text between some sort of marker, and then just changing the text between those markers is the sort of thing Regular Expressions were born to do! In the case you ask about here you would have to be a little careful about the order of execution, but effectively (assuming text starts black) you'd want to do a search for &quot;<(*)>&quot; and replace with a blue version, and then serach for &quot;\&quot;(*)\&quot;&quot; and replace with a green version (this is illustrative, and not necessarily accurate RegExp syntax). Regular Expressions are quite a large subject. The MS documentation on MSDN is somewhat limited, and it may be worthwhile buying a book on the subject (Regular Expressions are not a Microsoft invention, and are more-or-less a standard)
 
Hi,
I'll try the ExcludeUpdateRgn API call as soon as possible.

>I can't use it. Why ?????
>Not enough info to hazard a suggesting a solution. Can you >tell us more

I can :) Here are more information:
- The Microsoft Scripting Runtime (the scrrun.dll) is included.
- and now: if I type &quot;dim re as regexp&quot; the regexp isn't shown in the popup-combobox. (you know what i mean, don't you). The same with &quot;set re = new regexp&quot;.
- Whenever i run the code the compiler complains: &quot;user defined type not defined&quot; or something else (don't have the ide right here)

What's wrong ??

What about &quot;token objects&quot;. Does anybody know what's this ??

bateman23
 
NOT the scripting runtime. Regular Expressions have their own library called Microsoft VBScript Regular Expressions (oh, and a version number; on my machine the version is 5.5)
 
Sorry, but I'm back on these regular expressions. I have used the code posted here and changed it a little to come up with this small function that works very well:

Public Function ReplacePattern(rtf As RichTextBox, patrn As String, AmntRed As Integer, AmntGreen As Integer, AmntBlue As Integer) As String
Dim re As RegExp
Dim ReplacementString As String
Set re = New RegExp
re.Global = True
re.Pattern = &quot;(&quot; + patrn + &quot;)&quot;
ReplacementString = &quot;{\rtf1\ansi\ansicpg1252\deff0\deftab720{\fonttbl{\f0\fswiss MS Sans Serif;}{\f1\froman\fcharset2 Symbol;}{\f2\froman Times New Roman;}}{\colortbl\red&quot; & AmntRed & &quot;\green&quot; & AmntGreen & &quot;\blue&quot; & AmntBlue & &quot;;}\deflang2057\horzdoc{\*\fchars }{\*\lchars }\pard\plain\f2\fs20\cf1 $1\plain\f2\fs20}&quot;
rtf.TextRTF = re.Replace(rtf.TextRTF, ReplacementString)
End Function


All it does is color certain words in a rich text box a certain color. Into the function I pass the rich text box, a pipe (|) delimited list of words to colour (patrn) and the RGB colour of those words.

The replacement string is just the RichText representation of &quot;$1&quot; in the colour passed into the function.

This works fine when I pass a list of words to colour in one colour. HOWEVER when I try calling the function again on the same rich text box to colour some other words in a different colour its doesn't seem to do anything!

So this will colour some words in red but none of the blue words get coloured:

Call ReplacePattern(rtf, strPatternRedWords, 255, 0, 0)
Call ReplacePattern(rtf, strPatternBlueWords, 0, 0, 255)


And this will colour some words in blue but none of the red words get coloured:

Call ReplacePattern(rtf, strPatternBlueWords, 0, 0, 255)
Call ReplacePattern(rtf, strPatternRedWords, 255, 0, 0)


So I therefore know that both the lists of words to be coloured are correctly set out meaning that for some reason I can only call my function once successfully!

Any ideas or comments??!?

Many thanks,

elziko
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top