PDA

View Full Version : Controlling tab order of form elements



Sanchaz
06-21-2007, 01:12 PM
I work with the visually impaired. Recently, I?ve been asked to make a number of Word forms more accessible for VI users who use a screen reader when working in Word. I began with Help text to guide them through the forms, but then began exploring using macros to further enhance accessibility. (I?ve used the MsgBox function to display larger amounts of text than the Help text can handle.) Now I?m trying to enable dependent text fields conditionally so the user doesn?t have to tab through them (and hear the screen reader babble on about them) if the related checkbox is not checked.

I want to the macro to execute on exit from the checkbox control (I?m using the Word Form toolbar to build my forms):

So far, I?ve sketched out the following code, but am fairly sure this is way too simplistic to work. Also, what do I use to identify the form element - the bookmark name? Can you help me with this task?

Sub Macro1()
--if the checkbox is not checked, disable the text fields for input.
IF Me.Checkbox.value = False, then
textbox 1.enabled = False
textbox 2.enabled = False
End IF

End Sub

I also found a piece of code that I modified to turn off the tab stop on the text fields if the checkbox isn?t checked.
--if the checkbox isn?t checked, skip over the dependent text fields and move on to the next form element.

If Me.Checkbox.value = False, then
Textbox1.tabstop = false
Textbox2.tabstop = false
End If

I assume here that when the user presses TAB, the macro will run and jump to the next form control after taking the text fields out of the tab sequence, even though they?re enabled. Am I making a valid assumption?

geekgirlau
06-21-2007, 07:06 PM
You are correct, although my personal preference is to use enabled rather than tab stop - with tab stop, the user can still select the text box using the mouse and enter data in it. You can also streamline your code a little as follows:


textbox1.Enabled = Me.CheckBox.Value
textbox1.TabStop = Me.CheckBox.Value

Sanchaz
06-22-2007, 10:48 AM
Thanks for the validation. I finally came up with something that works, even if it isn't elegant. If the user tabs past the checkbox without selecting it, the dependent text fields are disabled and their contents are set to nothing. (in case they did choose the option and then changed their choice later).

On Exit macro:
If ActiveDocument.FormFields("Pub_Trans").CheckBox.Value = False Then
ActiveDocument.FormFields("Bus_Rate").Result = ""
ActiveDocument.FormFields("Bus_Rate").Enabled = False
ActiveDocument.FormFields("Bus_Units").Result = ""
ActiveDocument.FormFields("Bus_Units").Enabled = False
ActiveDocument.FormFields("Bus_Cost").Result = ""
ActiveDocument.FormFields("Bus_Cost").Enabled = False

I also found it necessary to ensure that the text fields were enabled on checkbox entry - again because user could go back and choose the option later:

On Entry macro:
ActiveDocument.FormFields("Bus_Rate").Enabled = True
ActiveDocument.FormFields("Bus_Units").Enabled = True
ActiveDocument.FormFields("Bus_Cost").Enabled = True

fumei
06-22-2007, 01:45 PM
May I suggest using objects? It makes coding cleaner, and easier.Dim oDocFF As FormFields
Set oDocFF = ActiveDocument.FormFields
If oDFocFF("Pub_Trans").CheckBox.Value = False Then
oDocFF("Bus_Rate").Result = ""
oDocFF("Bus_Rate").Enabled = False
oDocFF("Bus_Units").Result = ""
oDocFF("Bus_Units").Enabled = False
oDocFF("Bus_Cost").Result = ""
oDocFF("Bus_Cost").Enabled = False

It is even better to use With statements as well. Otherwise the parser has to check each instruction against the object model.Dim oDocFF As FormFields
Set oDocFF = ActiveDocument.FormFields
If oDFocFF("Pub_Trans").CheckBox.Value = False Then
With oDocFF("Bus_Rate")
.Result = ""
.Enabled = False
End With
With oDocFF("Bus_Units")
.Result = ""
.Enabled = False
End With
With oDocFF("Bus_Cost")
.Result = ""
.Enabled = False
End WithIn the second one, only THREE object request occur. This one request covers both .Result AND .Enabled

oDocFF("Bus_Rate")
oDocFF("Bus_Units")
oDocFF("Bus_Cost")

In the previous one, SIX requests are made.
oDocFF("Bus_Rate").Result = ""
oDocFF("Bus_Rate").Enabled = False
oDocFF("Bus_Units").Result = ""
oDocFF("Bus_Units").Enabled = False
oDocFF("Bus_Cost").Result = ""
oDocFF("Bus_Cost").Enabled = False

Further, by making the entire FormFields collection of the document ONE object (oDocFF), the VBA parser just needs to look at that one object. Otherwise, it has to:

1. Look at the ActiveDocument object;
2. Parse through its Collections and find the Formfield collection;
3. Parse through the Formfield collection for ActiveDocument.FormFields("Bus_Rate")
4. Parse through that object for .Result.

To action the next instruction it has to:

1. Look at the ActiveDocument object;
2. Parse through its Collections and find the Formfield collection;
3. Parse through the Formfield collection for ActiveDocument.FormFields("Bus_Rate")
4. Parse through that object for .Enabled.

It has to do that for EACH and EVERY instruction!

By making a Formfield object (containing ALL the formfields in the document), ALL step #1's, and #2 (six each...so 12 actions!) are not needed, or performed.

Your code:
21 object parsing; 7 instructions

My code:
4 object parsing; 9 instructions

It take much more work for VBA to check and parse objects that to act on instructions. Why? Because it is a computer, and it just DOES instructions. They either work, or they don't. When it parses an object's properties, it goes through them ALL.

Properties are in order within the class modules that make them.

So when VBA gets ActiveDocument.Formfields("Bus_Rate").Result, it goes:

ActiveDocument.AcceptAllRevisions...nope
ActiveDocument.AcceptAllRevisionsShown.....nope
ActiveDocument.Activate....nope
ActiveDocument.ActiveTheme......nope
.......
ActiveDocument.Fields...nope
ActiveDocument.FitToPagess...nope
ActiveDocument.Footnotes...nope
........
ActiveDocument.Formfields...ah, there we go.

Now....

Formfields("Abbb").....nope
Formfields("Acccb").....nope
Formfields("Bus_Rate").....ah, there we go


Now...
Formfields("Bus_Rate").Application........nope
Formfields("Bus_Rate").CalculateOnExit........nope
............
Formfields("Bus_Rate").Cut........nope
Formfields("Bus_Rate").Delete........nope
Formfields("Bus_Rate").DropDown........nope
......
Formfields("Bus_Rate").Result........ah, there we go.

See, there is a LOT going on that we don't think about (nor should we really, that is what parsers are for). That is what makes VBA a high level language. Yet, somewhere....all that stuff above IS going on.

So anything to reduce that activity is generally speaking a good thing. From a processing point of view, but also - once you get used to it.

oDocFF means something. It is a container of all the formfields in the document. And that is handy.

Like tables. Or bookmarks.

rant. rant. Too much coffee.

Sanchaz
06-22-2007, 02:08 PM
Thanks, Fumei. I knew there had to be a more elegant way to do this, but I'm just a hacker. Even so, I managed to impress my boss, who is blind. And I had fun figuring it out.

Thanks again!:dance: