Initializing Class Module, AutoExec, Changes to Normal.Dot

G

Greg

Appologize for the mulitple subjects, but the question is complex (a
least to my simple mind).

With some earlier stumbling and help from Jezebel, I have managed to
"manually" control the display of a menu item on a the Tables toolbar.
The item is enabled if the selection is in a table and disabled (or
dimmed if not). This mimics the behaviour of the builtin Table menu
items.

To achieve this, I have a regular VBA modue with the following code:

Option Explicit
Public myDynamicMenu As myClass1
________________________________________
Sub Register_MyClass1()
Set myDynamicMenu = New myClass1
Set myDynamicMenu.appWord = Word.Application
End Sub

and a Class module with the following code:

Option Explicit
Private mMenuOption As Office.CommandBarControl
_________________________________________
Public WithEvents appWord As Word.Application
Private Sub Class_Initialize()
Set mMenuOption = CommandBars("Table").Controls("Table Data")
End Sub
_________________________________________
Private Sub appWord_WindowSelectionChange(ByVal oSel As Selection)
mMenuOption.Enabled = oSel.Information(wdWithInTable)
End Sub

To enable the dynamic menu display, I run: Sub Register_MyClass1

So far so good.

The problems:

1. Jezebel had asked me "Is there some reason that I declared
"WithEvents appWord As Word.Application" as Public?

My answer is that this is the only way I can make the thing work, so I
suspect that I am doing something wrong. Can anyone explain what I am
doing wrong or how I should initialize the Class module to keep the
declarations Private?

2. I keep Tools>Options>Save>Prompt to save Normal.Template checked.
However, as this class module is changing the menu display dynamically
every time I click in and out of a table, it is flagging Normal.dot and
each time I quit Word I am promted to save Normal.dot. I know how to
set Normal as Saved with code, but that would mask all changes. Is
there some way to avoid this promt when you know that there was a
specific change? If not, then I suppose while this has been an
interesting exercise, the nuisance of the pop-up is not worth the gain
of a dynamic menu.

3. As I stated earlier, this process is now initiated manually by
running Register_MyClass1. If I call Register_MyClass1 (or move the
code from Register_MyClass1) to an AutoExec macro the application
generates the following error immediately after Word opens and the
selection changes:

Run time error '424' Object required. Can't move the focus to the
control because it is invisible, not enabled, or of the type that does
not accept the focus."

The error occurs on this line:

mMenuOption.Enabled = oSel.Information(wdWithInTable)

of the Class module code.

However, if I stet out the call from the AutoExec macro, let Word fully
open, then go back and unstet the call and step through the AutoExec
macro, then things work properly again.

Is this a timing issue or is there some way to wait until after Word is
fully opened before executing the call?

Thanks for any answers you can provide.
 
T

Tony Jollans

1. You can't - unless I've missed something glaringly obvious (which is
certainly possible). In order for a Property of a Class to be accessible
from outside the Class it must be Public. You can use a Friend Property Set
to contain the scope but Private won't work (unless the whole shebang is in
the ThisDocument module).

2. Save the saved state before your change and reset it afterwards to what
it was before ...

SavedState = NormalTemplate.Saved
CommandBars("Table").Controls("Table Data").Enabled = True
NormalTemplate.Saved = SavedState

(assuming your CustomizationContext is Normal)

3. This is more fun :) I'm guessing a bit but I think it is a timing issue.
Your custom control may not have been loaded when the ClassInitialize runs.
If so you may be able to use an OnTime to try again a second or two later -
some trial and error might be needed to get this right because you may find
that the WindowSelectionChange Event runs before your timer elapses so some
error trapping will be in order.
 
G

Greg

Tony,

Thanks.

1. All of the code is in Modules of Normal.Dot.

<You can use a Friend Property Set to contain the scope.

How is this done?

2. Ok, I will experiment with this suggestion.


3. I tried some timers and no joy. I did manage to avoid the errors,
by avoiding the Class Module Initialization bit. I have changed my
code as follows, and the error is gone:

In the Regular Module I now have:

Option Explicit
Public myDynamicMenu As myClass1
___________________________________
Sub Register_MyClass1()
Set myDynamicMenu = New myClass1
Set myDynamicMenu.appWord = Word.Application
End Sub

and in the Class Module I have:

Option Explicit
Public WithEvents appWord As Word.Application
____________________________________________________________
Private Sub appWord_WindowSelectionChange(ByVal oSel As Selection)
CommandBars("Table").Controls("Table Data").Enabled =
oSel.Information(wdWithInTable)
End Sub

Of course I could still be missing the obvious.
 
T

Tony Jollans

Hi Greg,

1. Just like any other Property Set - something like ...

In Class Module
===========
Private WithEvents appWord As Word.Application

Friend Property Set MyApp(HisApp As Word.Application)
Set appWord = HisApp
End Property

In AutoExec (or wherever)
==================
myClass.MyApp = Application

Using Friend limits the scope to the Project.

3. That should avoid the problem. If you're feeing brave you may also be
able to get round it with CommandBars Events.
 
G

Greg

Tony,

I'm sorry but I am getting terribly confused with the additon of myApp
and HisApp. Based on what I have now:


A Class Modue named myClass1 with the following code:

Option Explicit
Public WithEvents appWord As Word.Application
____________________________________________________________
Private Sub appWord_WindowSelectionChange(ByVal oSel As Selection)
Dim bSavedState As Boolean
bSavedState = NormalTemplate.Saved
CommandBars("Table").Controls("Table Data").Enabled =
oSel.Information(wdWithInTable)
NormalTemplate.Saved = bSavedState
End Sub

A regular module with:

Public myDynamicMenu As myClass1
____________________________________
Sub AutoExec()
Set myDynamicMenu = New myClass1
Set myDynamicMenu.appWord = Word.Application
End Sub

How would I revised to allow avoid the Public declarations in both the
Module and Class module and employ the FRIEND statement.

I am not feely brave, just very frustrated ;-) Thanks for all of the
help.
 
T

Tony Jollans

To do it 'properly' you should not expose variables in Class definitions -
that is they should all be Private. All exposure to Class Properties should
be via Property Let/Set/Get routines. These routines cannot be Private or no
code outside the Class would be able to use them - they can be Friend
(Project Scope) or Public (Application Scope). I know you know how to code
these because I've seen you post about them before, but here's a rundown :)

In your code you have a Public Variable which needs setting to the
Application in order for the Application Events to work - in my variation I
have a Private variable.

Yours:

Option Explicit
Public WithEvents appWord As Word.Application
'*****

Mine:

Option Explicit
Private WithEvents appWord As Word.Application
'*****

To set your public variable, any code anywhere can do:

Set myDynamicMenu = New myClass1
Set myDynamicMenu.appWord = Word.Application

To set my private variable, no code anywhere can do anything, so I provide a
Property Set routine (names changed from before to avoid confusion - I hope)

Property Set PrivateAppProperty(AppPassedByUser As Word.Application)
Set appWord = AppPassedByUser
End Property

This sets up a Property of myClass1 called PrivateAppProperty - which is of
type Word.Application. To set it, code elsewhere can use:

Set myDynamicMenu = New myClass1
Set myDynamicMenu.PrivateAppProperty = Word.Application
' ***************

In the Class definition, the value of this property is saved in the local,
private, variable called appWord - where, conveniently and coincidentally,
it is needed for the Application Events to fire.

By default the Property Set is declared as Public

Public Property Set PrivateAppProperty(AppPassedByUser As
Word.Application)
'*****

and it can be called from anywhere. As already noted it won't work as
Private but by, instead, declaring it as Friend ...

Friend Property Set PrivateAppProperty(AppPassedByUser As
Word.Application)
'*****

it can only be set from within the Project which isn't a great big deal with
Normal but could be with, perhaps, an AddIn.



All that said, it is common to do it as you have done with a Public
variable.
 
G

Greg Maxey

Tony,

Thanks for staying with me, but still no joy. I finally managed to get
passed an error on this line:

Set myDynamicMenu.appWord = Word.Application

Well not really, I simply started from scratch with a new blank document.
Here is what I did.

I create a module and a class module with out changing the names. I created
a new menu "New Menu" and put on that menu a control name "Test." In the
module, I have this code:

Sub SetClassModule()
Dim myDynamicMenu As Class1
Set myDynamicMenu = New Class1
Set myDynamicMenu.PrivateAppProperty = Word.Application
End Sub

In the Class modue, I have this code:

Option Explicit
Private WithEvents appWord As Word.Application
Friend Property Set PrivateAppProperty(AppPassedByUser As Word.Application)
Set appWord = AppPassedByUser
End Property

Private Sub appWord_WindowSelectionChange(ByVal oSel As Selection)
Dim bSavedState As Boolean
bSavedState = NormalTemplate.Saved
CommandBars("New Menu").Controls("Test").Enabled =
oSel.Information(wdWithInTable)
NormalTemplate.Saved = bSavedState
End Sub

When I step through the SetClassModule it does get passed the Set
..PrivateAppProperty line and through the Property Set procedure in the Class
module. Everything seems fine. However, the appWord)_WindowSelectionChange
doesn't work.

I realize the the Public method I have working now is good enough, but would
like to resolve and understand this method as well.

Thanks
 
G

Greg

Tony,

Jezebel has explained how to use Private declarations and the Class
Initialization event in a reply to my topic "Sanity Check." You might
be interested in having a look.
 
T

Tony Jollans

Hi Greg,

Probably my explanation. Sorry.

You have the myDynamicMenu Class1 object defined in the SetClassModule
procedure. This means that as soon as the procedure ends the object is
destroyed. In your original you have it declared at module level so it hangs
around long enough for the events within it to be fired - you need to do the
same here.

You might get away with making SetClassModule Static but I think that will
just be confusing :)
 
T

Tony Jollans

I've just looked at Jezebel's response in the other thread. Makes sense -
I've not done it like that, I've always set it from outside but if there is
no requirement (other than setting) for it to be visible outside the class
then keeping it private is better. Best ignore me and stick with people who
know what they're talking about :)
 
G

Greg

Tony,

Thanks. I got your method to work also.

Best ignore me and stick with people who know what they're talking
about :)

Unfortunately I am stuck to my own skin and need all the help I can
get.;-)
 

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