Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
Home
DiscussionsAccessExcelInfoPathOutlookPowerPointPublisherWord
DirectoryUser Groups
Related Topics
Outlook ExpressInternet ExplorerWindowsMS Server ProductsMore Topics ...

MS Office Forum / Word / Programming / October 2005

Tip: Looking for answers? Try searching our database.

Tech Eval

Thread view: 
Enable EMail Alerts  Start New Thread
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.
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.