MS Office Forum / Word / Programming / March 2006
Initializing Class Module, AutoExec, Changes to Normal.Dot
|
|
Thread rating:  |
Greg - 01 Mar 2006 17:23 GMT 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.
Tony Jollans - 01 Mar 2006 18:40 GMT 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.
-- Enjoy, Tony
> Appologize for the mulitple subjects, but the question is complex (a > least to my simple mind). [quoted text clipped - 77 lines] > > Thanks for any answers you can provide. Greg - 01 Mar 2006 18:54 GMT 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.
Tony Jollans - 01 Mar 2006 19:18 GMT 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.
-- Enjoy, Tony
> Tony, > [quoted text clipped - 33 lines] > > Of course I could still be missing the obvious. Greg - 01 Mar 2006 20:32 GMT 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.
Tony Jollans - 01 Mar 2006 21:28 GMT 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.
-- Enjoy, Tony
> Tony, > [quoted text clipped - 28 lines] > I am not feely brave, just very frustrated ;-) Thanks for all of the > help. Greg Maxey - 02 Mar 2006 00:54 GMT 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
 Signature Greg Maxey/Word MVP See: http://gregmaxey.mvps.org/word_tips.htm For some helpful tips using Word.
> To do it 'properly' you should not expose variables in Class > definitions - that is they should all be Private. All exposure to [quoted text clipped - 95 lines] >> I am not feely brave, just very frustrated ;-) Thanks for all of the >> help. Greg - 02 Mar 2006 13:35 GMT 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.
Tony Jollans - 02 Mar 2006 14:42 GMT 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 :)
-- Enjoy, Tony
> Tony, > [quoted text clipped - 147 lines] > >> I am not feely brave, just very frustrated ;-) Thanks for all of the > >> help. Tony Jollans - 02 Mar 2006 14:51 GMT 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 :)
-- Enjoy, Tony
> Tony, > [quoted text clipped - 147 lines] > >> I am not feely brave, just very frustrated ;-) Thanks for all of the > >> help. Greg - 02 Mar 2006 15:37 GMT 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.;-)
|
|
|