Enable/Disable Toolbar button

N

NZ VBA Developer

Word does it, so how can I do it?

I want to enable/disable a toolbar button depending on cursor
location/selection. For example, if I put the 'Restart Numbering' and
'Continue Numbering' buttons on the 'Formatting' toolbar, Word "knows" to
enable these buttons _only_ if the current selection is numbered. Any ideas
on how to replicate this functionality for the buttons on my own custom
toolbar so the macro assigned to the button can be run only in the correct
context?

(In fact, the 'Restart/Continue Numbering' functionality is the
functionality that I want to replicate, but I can't just add the native Word
buttons to my custom toolbar because this functionality doesn't work with
protected documents. So if somebody has a clever idea for getting around
this...)
 
J

Jay Freedman

If you name your macros RestartNumbering and ContinueNumbering, then
the built-in buttons (with their automatic enable/disable behavior)
will run the macros instead of the built-in commands. The macros will
also intercept the commands on the context (right-click) menu, which
appear only when the appropriate text is clicked.
http://www.word.mvps.org/FAQs/MacrosVBA/InterceptSavePrint.htm
 
N

NZ VBA Developer

Doh! Why didn't I think of that? That's exactly what I do to get around that
bloody Task Pane popping up whenever the 'File | New' button is clicked. I
have a simple one-liner - named 'FileNew' - that just displays the 'New'
dialog box.

However, I wonder if the same 'disable in protected documents' behaviour
will still apply... I'll give it a go and post the results one way or another.
 
N

NZ VBA Developer

Hmm... Not quite Jay...

Naming the macros as suggested does make it so they run from the 'native'
toolbar button or the right-click menu, but it still doesn't get around the
original problem. The native toolbar buttons and the right-click menu items
are still disabled when the document is protected.

What I need is a combination of the behaviour provided by the buttons for
the native functionality and the behaviour of buttons with macros assigned to
them: enabled even though the document is protected but only when the context
is correct. In fact, I'd be happy to use the native functionality rather than
my macro since all I'm trying to do is to give the users the ability to
restart/continue numbering, and the native functionality does this just fine
- just not when the doc is protected. All my macro does is change an argument
for the ApplyListTemplate method as follows:

Sub RestartNumbering()
Dim LT As ListTemplate
Set LT = Selection.Style.ListTemplate
Selection.Range.ListFormat.ApplyListTemplate ListTemplate:=LT,
ContinuePreviousList:=False
End Sub

Sub ContinueNumbering()
Dim LT As ListTemplate
Set LT = Selection.Style.ListTemplate
Selection.Range.ListFormat.ApplyListTemplate ListTemplate:=LT,
ContinuePreviousList:=True
End Sub

Is there a way to capture/monitor the 'selection change' event using VBA?
Seems to me that this is what Word is doing, and I've seen COM add-ins do the
same...
 
N

NZ VBA Developer

Jay,

Thanks for the tip. I've just about got it sussed now. Here's the code I'm
using. It seems to work OK and I haven't noticed any major hit in performance.

Private Sub oApp_WindowSelectionChange(ByVal Sel As Selection)
On Error Resume Next
If Sel.Type = wdSelectionIP Then
Dim SelStyleName As String
SelStyleName = Sel.Style.NameLocal
Select Case SelStyleName
Case "Heading 1", "Heading 2", "Heading 3", "Heading 4", "List
Number", "List Number 1", "Table List Number", "Table List Number 2"
With CommandBars("Toolkit")
.Controls(6).Enabled = True
.Controls(7).Enabled = True
End With
Case Else
With CommandBars("Toolkit")
.Controls(6).Enabled = False
.Controls(7).Enabled = False
End With
End Select
End If
End Sub


My first time writing a Class Module. It's got me thinking about other
possibilities...
 
S

Shauna Kelly

Hi Gordon

The WindowSelectionChange event won't fire when the user negotiates with the
cursor instead of the mouse, and it doesn't fire if the user clicks the
Document Map to move around. To get around that, you can use the built-in
functionality that Word has in its various buttons.

Find a built-in button that has the functionality you want. That is, find a
built-in button that turns on and off when you want yours to turn on and
off. (For example, if you want a button that is only enabled when the
insertion point is in a table, then choose one of the buttons on the Table
menu that operates as you want.)

Then *copy* the button you found to your own toolbar. You can then change
the icon and the displayed text to suit yourself. If you need more than one
button to be enabled and unenabled at the same time, but do different
things, then copy the *same* button (and change the icon and displayed text
to suit yourself).

Use the Immediate Pane to determine the ID of the button (let's say it's
296). Using the Immediate Pane, give your button(s) a .Tag (something
meaningful to you that identifies it as belonging to your project, let's say
"Gordon"). You may have several buttons with the same .ID and .Tag
combination. Give each button a separate .Parameter, which identifies each
one uniquely (let's say "Gordon_RestartNumbering" and
"Gordon_ContinueNumbering"). In my example here I used the useless names of
"Param1" and "Param 2".

So, in my example document I now have a custom toolbar named "Custom 1" with
two buttons. They both have the same .ID of 296. They both have a .Tag of
"Gordon". One has .Parameter "Param1" and the other is "Param2". And if I
play with Word, they turn on and off as I need them to.

Now use the following code:

1. In an ordinary Module:

Option Explicit

Public gMyButton As CMyButtons



2. In the ThisDocument class:

Option Explicit

Private Sub Document_New()
Set gMyButton = New CMyButtons
End Sub

Private Sub Document_Open()
Set gMyButton = New CMyButtons
End Sub



3. In a Class module named CMyButtons

Option Explicit

'A variable to hold a reference to any button with your combination
'of ID and Tag
Private WithEvents mMyButton As Office.CommandBarButton

Private Sub Class_Initialize()
'Set mMyButton to refer to a button with your combination of ID
'and Tag. Even though you may have many buttons with the same ID
'and Tag combination, you only need to 'hook' one of them here.

'This way is fast
Set mMyButton = CommandBars("Custom 1").Controls(1)

'OR....
'This way is safe
Set mMyButton = CommandBars.FindControl(Type:=msoControlButton, ID:=296,
Tag:="Gordon")

End Sub

Private Sub mMyButton_Click(ByVal Ctrl As Office.CommandBarButton,
CancelDefault As Boolean)

'This fires when the user clicks one of your buttons with your combination
'of ID and Tag

If Ctrl.Tag = "Gordon" And Ctrl.ID = 296 Then
'Strictly speaking you don't need this whole If ... .Tag *and* .ID
thing here
'but I find it enormously helpful documentation!

Select Case Ctrl.Parameter
Case "Param1"
MsgBox "You clicked my Param1 button"
'Run the appropriate code here

Case "Param2"
MsgBox "You clicked my Param2 button"
'Run the appropriate code here
End Select

'prevent Word from doing the default action
'for the button
CancelDefault = True

End If

End Sub



Create a new document from your template and watch the magic happen!

For the record, any .OnAction value of the button(s) is ignored when you
hook buttons like this.

Now, go buy a copy of Professional Excel Development by Bullen, Bovey and
Green where you'll read a much better description of this than I have given.
Ignore the early chapters about Excel, if you want to. The book is worth it
for the latter chapters and it applies equally well to Word as to Excel.

Hope this helps.

Shauna Kelly. Microsoft MVP.
http://www.shaunakelly.com/word

(England v the Springboks. Who would have thought?)
 
N

NZ VBA Developer

~sigh~

You're really gonna make me work for this one, aren't you Shauna. I'm
tempted to just make the buttons enabled all the time and pop a message box
if the user clicks on one when they're not s'posed to. <g>

Thanks for all of the info. I was wondering why the buttons weren't enabling
when I expected them to, but I haven't had the time to investigate. I'll give
this a go tonight or tomorrow and let you know how I get on. (I'll also take
a trip to Borders and look for that 'recommended reading' as well.)

Sleep is definitely overrated!
 
N

NZ VBA Developer

Shauna,

I'm just re-reading your post to make sure I understand. Are you saying that
the WindowSelectionChange event isn't triggered when the cursor is
repositioned using the arrow keys? Or clicks through a cross-reference, such
as a TOC entry? Cuz it seems to be working OK for me - well at least the
arrow keys are. I haven't tried using the Document Map yet to see what
happens, but since my 'target audience' isn't especially Word-savvy, I may
just leave it alone; I'm not sure anyone at the client site even knows what
the Document Map is.

The only time I noticed the buttons not behaving as expected was when I
first applied a numbered style to a selection - altho they may have been
behaving oddly when I was typing as well. And even then I'm not sure; I'll
have to experiment a bit more when I have some time later on.

In any case, I do appreciate the information, and if it turns out that I
really do need it, I'm sure I'll appreciate it even more!
 
S

Shauna Kelly

Hi

Sorry, I kind of said one thing and meant something very different.

The WindowSelectionChange doesn't fire when the user types letters or uses
any function keys or shortcut keys. For example, if the user applies a style
to the selection, the WindowSelectionChange event won't fire.

Since you want to detect when the insertion point is in a numbered or
bulleted paragraph, then the WindowSelectionChange won't do it for you. If
style Heading 1 is numbered, the user can apply that style, and thus the
paragraph at the insertion point will be numbered, without the event firing.
The same applies to applying bulleted styles and even (gasp!) using the
bullets or numbering buttons on the toolbar. And, the same applies in
reverse, if the user applies an un-bulleted style to a paragraph that has
bullets.

Hope this helps.

Shauna Kelly. Microsoft MVP.
http://www.shaunakelly.com/word
 
N

NZ VBA Developer

Aha! I'm with you now. And that's exactly the kind of behaviour I've been
seeing - typing in a numbered paragraph but the buttons are still disabled.
So it looks like it's going to be a looooong night for me as this project is
due to roll out over night tomorrow.

Now for the hard part: finding a 'doner' button - one that enables/disables
in a numbered paragraph in a protected document...

And for the record, we _don't_ use the bullets and numbering buttons. We
replace them with buttons that look the same but call a macro that applies a
bulleted or numbered style. Them buttons is the DEBIL!!!
 
N

NZ VBA Developer

No joy, Shauna. As I suspected, there's no 'doner' button that I can use as
my starting point - no button that's enabled only when the selection point is
in a numbered paragraph AND the document is protected. Looks like I'm just
going to have to go with the WindowSelectChange event code I wrote
previously. At least this works some of the time.

However, your efforts weren't totally wasted. I have another button - the
one for rerunning the macros for creating the doc - that I want to be enabled
only when the attached template is one of the templates that I developed. The
information you provided has given me a lead on how to make this work.

Thanks again for all your help.
 
N

NZ VBA Developer

Unfortunately, no. That one is disabled if the document is protected, so it
stays disabled even if I 're-purpose' it. And that was what got me started
down this track anyway. If the built-in Restart Numbering and Continue
Numbering worked in protected documents then I'd just use them instead of
trying to develop my own. Catch-22 really.

And then I ran into another problem that made me bag the whole works.
Changing the state of a toolbar button effectively makes the global template
'dirty' so the user gets prompted to save the template on exiting Word. I
tried forcing a save of the global template after changing the button state
but that unloaded the template - and also automagically made it so the mouse
didn't work for changing the selection; I could only use the arrow keys, and
then because the template was no longer loaded, the WindowSelectionChange
event didn't get captured. GAHHH!!!

I just went back to the 'ambulance at the bottom' technique. Now if the user
clicks on one of the buttons when the cursor isn't in a numbered paragraph
(or the selection type is wdSelectionIP), it pops a message box. I figure it
will only take a couple of times for the users to work out how to use the
functionality. Not ideal I know, but the deadline on the project is 4pm
today... Maybe when I have a bit more time I'll see if I can get it working
properly. I'm sure there'll be maintenance and upgrade releases at some
point, and I can slip the 'proper' functionality in under one of them.

Anyway, thanks again for all your help. I really do appreciate it. And even
if I can't use this new knowledge on this project, I'm sure it will come in
handy at some other point.
 
S

Shauna Kelly

On dirtying the template:
ActiveDocument.AttachedTemplate.Saved = True
will prevent "djawantsave?" prompts without actually saving the template and
any attendant worries that causes.

Hope this helps for next time!

Cheers

Shauna

Shauna Kelly. Microsoft MVP.
http://www.shaunakelly.com/word
 
N

NZ VBA Developer

Hmm... That just might work! Or at least a variation of it might...

From memory (I don't have the code in front of me to check), when I was
trying to save the global template after every change of button status, I had
to go through the Templates collection: Templates("Toolkit.dot").Save. If I
combine this with your suggestion - Templates("Toolkit.dot").Saved = True - I
may be home and hosed. I just ran a quick test and the VBE
AutoComplete/Intellisense functionality (whatever it's called) does
allow/support this code. My only concern is that it will unload the add-in
again...

I'll have a play around tonight and if it works, I'll slip it into the RTM
version, which isn't due to be released until next week. (It was just the RC
version that had to be in by 4 yesterday.)

Thanks Shauna! You're alright - even for an Ozzie! Ozzie! Ozzie! OY! OY! OY!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top