I have made two ".docm" files that have proper button and "_click()" procedure respectively. The first "myDocument1" works well, no problem at all. The other one "myDocument2" does not. I guess, I need "onAction" or something, but don't know any more. Pls tell me what to do.
The first one has its codes in "ThisDocument" and The second one has in "myModule". The code is single line only "Msgbox OK" both of them.
I'd like to see that it works at the "myModule" not in "ThisDocumet" in case of second file.
[EDIT] I'd like to add some line of VBA codes that I've used when I made this Button(s). That is not the best routine (wow, a guru says that is even depricated....), but the only route that worked for me after long web searching.
Dim myButton
Set myButton = Selection.InlineShapes.AddOLEControl(ClassType:="Forms.CommandButton.1")
myButton.OLEFormat.Object.TakeFocusOnClick = False
myButton.OLEFormat.Object.Width = 100
Dim myCompo
Set myCompo = ActiveDocument.VBProject.VBComponents.Add(1)
myCompo.Name = "myModule"
Dim myModu
Set myModu = myCompo.CodeModule
myModu.InsertLines 1, _
"Sub " & myButton.OLEFormat.Object.Name & "_Click()" & vbCr & vbCr & _
" Msgbox ""OK""" & vbCr & vbCr & _
"End Sub"
and adding another screen capture, trying to make ClickEvent possible but not attained its goal.
The reason this does not work is because this type of Command Button is an ActiveX control, i.e. a COM control, that is contained within the document surface. Word expects event code for the object to be in the ThisDocument
module and automatically connects that Event code to the COmmandButton1 object when (say) it opens the document.
That's why in the VB Editor, when you have the code open for the ThisDocument
module, you will see in the dropdown box above the code, on the left hand side, (General)
and CommandButton1
. If you select CommandButton1
in that dropdown, the other dropdown displays the available events. Word uses a naming convention to generate the appropriate Sub for the event using the pattern objectname_eventname
- in this case, CommandButton1_Click
.
In any other module, Word doesn't recognise the name CommandButton1
as having anything to do with the object in the document surface, and doesn't make that connection automatically.
So, if you open myModule and look at the equivalent dropdown, all you will see is (General)
. Putting a routine called CommandButton1_Click
in the module just means that you have a regular subroutine. Word does not look at the name, recognise its pattern, and connect it to the actual object in the document. So that routine is not called when you click the Button.
In fact you can put ActiveX control code in a routine other than ThisDocument, but I certainly don't know what all the consequences of doing that might be. Personally, I think it involves enough additional complication thaat I would leave the code in ThisDocument
unless I absolutely had to put it somewhere else.
For one thing ActiveX controls are legacy controls (possibly even deprecated). They don't work in any version of Word except Windows Desktop Word. So there is a case for using something else instead.
But if you do want to put event code elsewhere, you can do it this way:
The code has to be in a Class Module (not an ordinary Module). Let's say you create one called myClass
.
In myClass
, declare a variable like this:
Public WithEvents cb As MSForms.CommandButton
(You can't use WithEvents
in an ordinary module)
Then you should be able to find "cb" in the dropdown above the code window, and you can create its _Click
event handler, e.g.
Sub CommandButton1_Click()
Msgbox "OK"
End Sub
But then your code has to create an object of type myClass
and connect its .cb
variable to the actual CommandButton1
object in your document. So you need to call something, somewhere that does something like this:
Dim myC As myClass
Sub CreateMyClass()
Set myC = New myCLass
Set myC.cb = ThisDocument.CommandButton1
End Sub
and you probably need something like this in the same module:
Sub TearDownMyClass()
Set myC.cb = Nothing
Set myC = Nothing
End Sub
You could for example put that code in ThisDocument
and use a Document Open event or routine to call CreateMyClass
.
Or if you are trying to take advantage of the facilities available in Class Modules, you could hide the cb
variable, and write your class module like this:
' cb not visible outside the class module.
Private WithEvents cb As MSForms.CommandButton
Public Property Set myButton(myButton As MSForms.CommandButton)
Set cb = myButton
End Property
Public Property Get myButton() As MSForms.CommandButton
Set myButton = cb
End Property
Private Sub cb_Click()
MsgBox "OK"
End Sub
Private Sub Class_Initialize()
End Sub
Private Sub Class_Terminate()
Set Me.myButton = Nothing
End Sub
Your other code could be like this:
Dim myC As myClass
Sub CreateMyClass()
Set myC = New myCLass
Set myC.myButton = ThisDocument.CommandButton1
End Sub
Sub TearDownMyClass()
Set myC = Nothing
End Sub
or, if you only need your class to work specifically with CommandButton1
, you could use this in the class
Private Sub Class_Initialize()
' You can only do this because CommandButton1 is visible from this module
Set myButton = ThisDocument.CommandButton1
End Sub
then in your ordinary module,
Sub CreateMyClass()
Set myC = New myCLass
End Sub
None of this stops Word from calling any relevant code in ThisDocument
as well. So if you have this new class code and you leave your CommandButton1_Click
code in ThisDocument
, Word should pop up two message boxes when you click the Command Button.
If you use either of the other two types of control in your document (legacy Form Fields, which are also available on Mac Word, or Content Controls, which are partly available on Mac Word, be aware the coding needs to be quite different.
Incidentally, if you are trying to write code that encapsulates objects and you like the idea of using private class variables (cf. cb) and property let/set/get statements to access them, you can use those statements in ThisDocument
because it is a Class Module.
Here are some screenshots from the VB Editor: