MS Office Forum / Word / Programming / October 2005
Tech Eval
|
|
Thread rating:  |
Greg Maxey - 14 Oct 2005 00:54 GMT Hi,
I have a textbox in a userform that I am trying to limit the entries (real time) to to characters allowed in a bookmark name (e.g., alpha characters, numbers, "_"). The first character must me a letter and the maximum length is 40 characters. I am also replacing spacebar entris (after the first character) with "_".
The code below seems to be working but I was wondering if there was a way to abbreviate "Me.TextBox1.Text" in some way and as I built this on the fly I suspect that I have driven a tack with a ten pound hammer and have overlooked a more graceful way of achieving the desired result.
Any ideas for improvement are appreciated.
Private Sub TextBox1_Change() testChar = Right(Me.TextBox1.Text, 1) If Len(Me.TextBox1.Text) = 1 Then If InStr(" 0123456789", testChar) > 0 Then Me.TextBox1.Text = Left(Me.TextBox1.Text, Len(Me.TextBox1.Text) - 1) MsgBox "Bookmark name must begin with a" _ & " letter.", vbInformation & vbOKOnly, "Invalid Character" End If End If On Error Resume Next If InStr(Chr$(32), testChar) > 0 Then Me.TextBox1.Text = Left(Me.TextBox1.Text, Len(Me.TextBox1.Text) - 1) _ & Chr$(95) End If On Error GoTo 0 On Error Resume Next If InStr(Chr$(44) & " `~!@#$%^&*()-+={}[]|?/><:;""", testChar) > 0 Then Me.TextBox1.Text = Left(Me.TextBox1.Text, Len(Me.TextBox1.Text) - 1) End If On Error GoTo 0 If Len(Me.TextBox1.Text) > 40 Then MsgBox "Bookmark name is limited to 40 characters.", _ vbInformation & vbOKOnly, "Limit" Me.TextBox1.Text = Left(Me.TextBox1.Text, 40) End If End Sub
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
Shell - 14 Oct 2005 02:02 GMT In response to the post: On Thu, 13 Oct 2005 19:54:42 -0400, "Greg Maxey" <gmaxey@mvps.OscarRomeoGolf> stated...and I replied:
>Hi, > [quoted text clipped - 37 lines] >End If >End Sub Hahaha, yes Greg, the .text does add a bit of bulk to a line of code. Here's a bit of standards for you regarding the use of the .text property.
First, most (maybe all) controls that use .text or .value have those properties set as the "default" property. What this means is, if you reference the control without including a property name (such as .text) you'll be given the .text property.
So, here's how that is used by standard...when setting a value into a control with a default property, you do not need to include the .text property name (i.e. Me.txtBox = "My value"). When getting the value from a control (i.e. txtValue = Me.txtBox.Text) use the property name...always, regardless of the existance of a default property.
With this in mind, your code above can be shrunk some by removing the ".text" from those 3 "If InStr" segments.
From this: Me.TextBox1.Text = Left(Me.TextBox1.Text, Len(Me.TextBox1.Text) - 1) to this: Me.TextBox1 = Left(Me.TextBox1.Text, Len(Me.TextBox1.Text) - 1)
Hope this helps, even if only a little, Shell
Greg Maxey - 14 Oct 2005 03:24 GMT Shell,
Every little bit helps. Thanks.
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
> In response to the post: > [quoted text clipped - 67 lines] > Hope this helps, even if only a little, > Shell Jonathan West - 14 Oct 2005 14:15 GMT > First, most (maybe all) controls that use .text or .value have those > properties set as the "default" property. What this means is, if you [quoted text clipped - 6 lines] > from a control (i.e. txtValue = Me.txtBox.Text) use the property > name...always, regardless of the existance of a default property. Shell, if you have code that you want to remain portable through future versions of Word, I would hesitate to rely on default properties in this way. VB.NET does not support default properties like this, and if VBA is ever replaced in Office by an embedded form of VB.NET, then relying on default properties may make it harder to port the code.
This applies equally to both reading and writing the property value.
Your proposed code isn't consistent in the way you use default properties - you rely on them when setting the property value but not when reading it.
 Signature Regards Jonathan West - Word MVP www.intelligentdocuments.co.uk Please reply to the newsgroup Keep your VBA code safe, sign the ClassicVB petition www.classicvb.org
Jay Freedman - 14 Oct 2005 03:05 GMT Hi Greg,
The biggest gain in both space and speed comes from using the With ... End With construction to minimize the use of "dots" (membership operators). In this case, put 'With Me.Textbox1' at the beginning of the routine and 'End With' at the end, and remove all occurrences of Me.Textbox1 in between. Not only does it shorten the code, but the interpreter doesn't have to "dereference" the object over and over so it runs faster.
You can also use the Like operator to do pattern matching on the strings instead of If Instr(). I'm not sure about the comparative speeds, but the patterns are shorter and less confusing than lists of characters.
Also, you have a bit of a logic problem when you test for invalid characters. The valid characters are just upper/lower case letters, numbers, and underscores. Your list of invalid characters is massively incomplete -- for example, ¶ isn't valid but it isn't in your list. It's better to use the Like operator with the ! qualifier that says "if testChar is *not* like this list of *valid* characters..."
Finally, let me whack you upside the head for not declaring your variable. <g>
So here's my version:
Private Sub TextBox1_Change() Dim testChar As String
With Me.TextBox1 testChar = Right(.Text, 1) If Len(.Text) = 1 Then If (testChar Like "[ 0-9_]") Then .Text = "" MsgBox "Bookmark name must begin with a letter.", _ vbInformation & vbOKOnly, "Invalid Character" End If End If On Error Resume Next If (" " = testChar) Then .Text = Left(.Text, Len(.Text) - 1) & "_" testChar = "_" End If On Error GoTo 0 On Error Resume Next If (testChar Like "[!A-Za-z0-9_]") Then .Text = Left(.Text, Len(.Text) - 1) End If On Error GoTo 0 If Len(.Text) > 40 Then MsgBox "Bookmark name is limited to 40 characters.", _ vbInformation & vbOKOnly, "Limit" .Text = Left(.Text, 40) End If End With End Sub
-- Regards, Jay Freedman Microsoft Word MVP FAQ: http://word.mvps.org
>Hi, > [quoted text clipped - 37 lines] >End If >End Sub Jean-Guy Marcil - 14 Oct 2005 03:38 GMT Jay Freedman was telling us: Jay Freedman nous racontait que :
> Hi Greg,
> Finally, let me whack you upside the head for not declaring your > variable. <g> LOL I meant to write about this point as well... So consider yourself doubly whacked!
:-)
 Signature Salut! _______________________________________ Jean-Guy Marcil - Word MVP jmarcilREMOVE@CAPSsympatico.caTHISTOO Word MVP site: http://www.word.mvps.org
Greg Maxey - 14 Oct 2005 04:29 GMT Oh course Jay. Another case of not seeing the tree for the forrest. Thanks.
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
> Hi Greg, > [quoted text clipped - 97 lines] >> End If >> End Sub Jean-Guy Marcil - 14 Oct 2005 03:35 GMT Greg Maxey was telling us: Greg Maxey nous racontait que :
> Hi, > [quoted text clipped - 10 lines] > desired result. > Any ideas for improvement are appreciated. I guess it boils down to personal taste, but I prefer to code against a finite list of what I want as opposed to coding against an infinite list of what I do not want. I mean, in this case, all I want is "_", letters or numbers, that's it. Anything else I do not want. I think this solves the On Error Resume Next problem, no?
Also, in your code, as soon as you change the content of the textbox, the Change event is recursively called. Since you are making the change, you know the change is good, so you do not have to let the code run twice to test this new change. Also, you can run into problems. For example, in your code, the block: If InStr(Chr$(32), testChar) > 0 Then changes " " for "_", but since there is a change if the user types a " ", it called the Change event again, and somehow, this second run removed the "_" (because of If InStr(Chr$(44) & " `~!@#$%^&*()-+={}[]|?/><:;""", testChar) > 0 Then I think). I could not get the code to replace the " " with a "_" until I removed the second Change even pass. and exited the Sub (The other If blocks are not needed if a pattern is found).
Finally, I prefer starting the code with a "With" block to avoid typing "Me.TextBox1" over and over.
So, with these in mind, my version is as follows:
'_______________________________________ Option Explicit
Public SkipChangeBool As Boolean
'_______________________________________ Private Sub TextBox1_Change()
If SkipChangeBool Then Exit Sub CheckData
End Sub '_______________________________________
'_______________________________________ Private Sub CheckData()
Dim TestChar As String
With Me.TextBox1 TestChar = Right(.Text, 1) If TestChar = "" Then Exit Sub 'In case user backspaces back to the beginning If Len(.Text) = 1 Then If Not .Text Like "[A-z]" Then 'And Not .Text Like "_" Then SkipChangeBool = True .Text = Left(.Text, Len(.Text) - 1) SkipChangeBool = False MsgBox "Bookmark name must begin with a" _ & " letter.", vbInformation & vbOKOnly, "Invalid Character" Exit Sub End If End If If TestChar Like " " Then SkipChangeBool = True .Text = Left(.Text, Len(.Text) - 1) & "_" SkipChangeBool = False Exit Sub End If If Not TestChar Like "[A-z]" And Not TestChar Like "_" _ And Not TestChar Like "[0-9]" Then SkipChangeBool = True .Text = Left(.Text, Len(.Text) - 1) SkipChangeBool = False Exit Sub End If If Len(.Text) > 40 Then SkipChangeBool = True MsgBox "Bookmark name is limited to 40 characters.", _ vbInformation & vbOKOnly, "Limit" .Text = Left(.Text, 40) SkipChangeBool = False Exit Sub End If End With
End Sub '_______________________________________
Longish, but easy to manage, no?
 Signature Salut! _______________________________________ Jean-Guy Marcil - Word MVP jmarcilREMOVE@CAPSsympatico.caTHISTOO Word MVP site: http://www.word.mvps.org
Greg Maxey - 14 Oct 2005 04:48 GMT JGM,
While I couldn't duplicate the problems you encountered in my code, I see the merit in yours. Thanks.
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
> Greg Maxey was telling us: > Greg Maxey nous racontait que : [quoted text clipped - 99 lines] > > Longish, but easy to manage, no? Jonathan West - 14 Oct 2005 14:46 GMT Hi Greg,
1. Reading and writing the Me.TextBox1.Text property is relatively slow. Manipulating external objects usually is. I would suggest you load the value into a string variable, do all the manipulation you want on the variable, and write it back to the textbox at the end.
2. It is quicker and easier to use a literal for a quote mark rather than Chr$(32). putting a pair of double quotes in a string literal places a double quote character in the string. You can do the same for Chr(95) and Chr(44). if you can't find them, then you can print the character into the immediate window and copy & paste it to the relevant place in the code.
Private Sub TextBox1_Change() Dim strIn as String Dim testChar as String
strIn = Me.TextBox1.Text testChar = Right(strIn, 1) If Len(strIn) = 1 Then If InStr(" 0123456789", testChar) > 0 Then strIn = Left(strIn, Len(strIn) - 1) MsgBox "Bookmark name must begin with a" _ & " letter.", vbInformation & vbOKOnly, "Invalid Character" End If End If On Error Resume Next If InStr("""", testChar) > 0 Then strIn = Left(strIn, Len(strIn) - 1) & "_" End If On Error GoTo 0 On Error Resume Next If InStr(", `~!@#$%^&*()-+={}[]|?/><:;""", testChar) > 0 Then strIn = Left(strIn, Len(strIn) - 1) End If On Error GoTo 0 If Len(strIn) > 40 Then MsgBox "Bookmark name is limited to 40 characters.", _ vbInformation & vbOKOnly, "Limit" strIn = Left(strIn, 40) End If Me.TextBox1.Text = strIn End Sub
 Signature Regards Jonathan West - Word MVP www.intelligentdocuments.co.uk Please reply to the newsgroup Keep your VBA code safe, sign the ClassicVB petition www.classicvb.org
--
> Hi, > [quoted text clipped - 37 lines] > End If > End Sub Greg - 14 Oct 2005 16:36 GMT Jonathan,
Copy all. Actually I am trying to replace " " (a space) with a "_" underscore instead of a """ with an underscore. Thanks.
Tony Jollans - 15 Oct 2005 01:38 GMT Can I suggest a completely different approach?
I don't know about the performance of this compared to yours but it ought to be totally reliable even if the rules change (like, say, a new release allowing longer names). Rather than trying to make sure you use the same rules as Word, let Word do the validation for you - try and create a bookmark using the contents of the textbox.
There are a couple of points to watch out for:
- The bookmark might already exist - but if it does it's a valid name
- Word truncates supplied names to 40 characters before doing anything else
There is also a problem with your original logic (which I have tried to address in the code below) in that a change is not necessarily a single character change. I have also included Jean-Guy's suggestion and added a reset of the global switch which would otherwise remain True once set.
I've probably made some silly mistake somewhere
Option Explicit
Dim TextboxOldText As String Dim TextboxReset As Boolean
Private Sub TextBox1_Change()
Dim TextboxNewText As String
If TextboxReset = True Then TextboxReset = False Exit Sub End If
TextboxNewText = Me.TextBox1.Text
If ValidBookmark(Me.TextBox1.Text) Then TextboxOldText = TextboxNewText Else 'MsgBox "Invalid Input" TextboxReset = True Me.TextBox1.Text = TextboxOldText End If
End Sub
Private Function ValidBookmark(TestName As String) As Boolean
Dim Temp As Variant
If Not ActiveDocument.Bookmarks.Exists(TestName) Then ValidBookmark = True Else On Error Resume Next Temp = ActiveDocument.Bookmarks.Add(TestName) Select Case True Case IsEmpty(Temp) ValidBookmark = False Case Temp <> TestName ActiveDocument.Bookmarks(TestName).Delete ValidBookmark = False Case Else ActiveDocument.Bookmarks(TestName).Delete ValidBookmark = True End Select End If
End Function
-- Enjoy, Tony
> Hi, > [quoted text clipped - 42 lines] > http://gregmaxey.mvps.org/word_tips.htm > For some helpful tips using Word. Greg Maxey - 15 Oct 2005 02:24 GMT Tony,
Interesting work and you make a good point about Word changing. The beef I have always had with the built-in dialog box is that it will let you type invalid characters in bliss. I have a habit of looking at what I type, not what changes state (add button dimming down below). I want a field that will only allow valid characters in real time. Thanks for posting your method. I will study it in more detail as there is always something to learn.
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
> Can I suggest a completely different approach? > [quoted text clipped - 123 lines] >> http://gregmaxey.mvps.org/word_tips.htm >> For some helpful tips using Word. Tony Jollans - 15 Oct 2005 08:03 GMT Hi Greg,
I think, maybe, you've misunderstood. My code would replace yours. It does the same job - real time validation/correction - just using a different technique.
-- Enjoy, Tony
> Tony, > [quoted text clipped - 138 lines] > >> http://gregmaxey.mvps.org/word_tips.htm > >> For some helpful tips using Word. Greg Maxey - 15 Oct 2005 13:53 GMT Tony,
I must have. Yesterday was a long day made longer by mandatory fun (a military social function) in the evening. I had spent so much (too much) time on that element of the procedure the night before and was anxious to move on to something else. I will give it another look and the time it deserves after I fix a few other things that are in a less advanced state. Thanks.
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
> Hi Greg, > [quoted text clipped - 161 lines] >> >> http://gregmaxey.mvps.org/word_tips.htm >> >> For some helpful tips using Word.
|
|
|