Class Event for Multiple Controls

kt1978

Registered User.
Local time
Today, 21:21
Joined
Jan 15, 2011
Messages
43
Hi

I've got this code (which I can't remember where from now, but I don't claim to have done it).

Basically I have a class event that works for all controls on any userform.

Its all working fine but I can't add an event to trap the click or mouse move of the userform itself.

Basically so when the cursor moves away from a control the label clears.

User Form Code

Code:
Option Compare Database
Option Explicit
'Define a collection and initialise the commandbutton event class
Private col As New Collection
Private newCmd As New clsEvents

Private Sub Form_Close()
  Set col = Nothing
  Set newCmd = Nothing
End Sub

Private Sub Form_Load()

  ' load all the commandbuttons of the form to one Eventhandler
  Dim ctl As Control
  ' loop through all controls

  For Each ctl In Me.Controls
    'check if control is a commandbutton
      'set a new reference to the new Eventclass
      Set newCmd = New clsEvents
      'store the commandbutton to the eventclass
      Set newCmd.CmdBtn = ctl
      'store the eventclass in a collection to keep them alive
      col.Add newCmd, ctl.Name
   'End If
  Next ctl

End Sub

And here is the class Module
Code:
Option Compare Database
Option Explicit
'catch the event of Commandbuttons
Private WithEvents C0 As SubForm
'Private WithEvents C1 As CheckBox
'Private WithEvents C2 As ComboBox
Private WithEvents C3 As CommandButton
'Private WithEvents C4 As Frame
Private WithEvents C5 As Label
'Private WithEvents C6 As Image
'Private WithEvents C7 As ListBox
'Private WithEvents C8 As MultiPage
Private WithEvents C9 As OptionButton
'Private WithEvents C10 As ScrollBar
'Private WithEvents C11 As SpinButton
'Private WithEvents C12 As TabStrip
Private WithEvents C13 As TextBox
'Private WithEvents C14 As ToggleButton
Private WithEvents C15 As Form

Dim myFrm As Form
Dim myObj As Object
Dim myCtl As Object
Private ParentForm As Object

'which commandbutton should have a common Eventhandling
Public Property Set CmdBtn(c As Object)
    
    Set ParentForm = c.Parent
  
    Select Case TypeName(c)
      Case "Userform": Set C0 = c
      Case "CommandButton": Set C3 = c
      Case "Label": Set C5 = c
      Case "OptionButton": Set C9 = c
      Case "TextBox": Set C13 = c
      Case Else: Exit Property
    End Select
  Set myObj = c
  c.OnClick = "[Event Procedure]"
  c.OnMouseMove = "[Event Procedure]"
  On Error Resume Next
  c.GotFocus = "[Event Procedure]"
  On Error GoTo 0
  
End Property

Public Property Set UsrFrm(c As Form)
    
  Set ParentForm = c
  Set C15 = c

  Set myFrm = c
  c.OnClick = "[Event Procedure]"
  c.OnMouseMove = "[Event Procedure]"
  
End Property

Private Sub Class_Terminate()
    Set C0 = Nothing
    Set C3 = Nothing
    Set C5 = Nothing
    Set C13 = Nothing
    Set C15 = Nothing
End Sub

Private Sub C3_Click()
  ParentForm.Label1.Caption = "You clicked: " & myObj.Name & " with Caption " & myObj.Caption
End Sub
Private Sub C3_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
  ParentForm.Label1.Caption = "You're over: " & myObj.Name & " with Caption " & myObj.Caption
End Sub

Private Sub C5_Click()
  ParentForm.Label1.Caption = "You clicked: " & myObj.Name & " with Caption " & myObj.Caption
End Sub
Private Sub C5_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
  ParentForm.Label1.Caption = "You're over: " & myObj.Name & " with Caption " & myObj.Caption
End Sub

Any help appreciated...

Thanks
 
See how your loop here . . .
Code:
  For Each ctl In Me.Controls
      Set newCmd = New clsEvents
      Set newCmd.CmdBtn = ctl
      col.Add newCmd, ctl.Name
  Next ctl
. . . only creates an event handler class for members of the controls collection of the form? Well, maybe you need to create a handler for the form itself, maybe like . . .
Code:
set newCmd = new clsEvents
[COLOR="Green"]'here we want to pass the form itself into a new instance of the clsEvents controller class[/COLOR]
set newCmd.CmdBtn = me  [COLOR="Green"]'CmdBtn actually takes an object type, so poorly named[/COLOR]
col.add newCmd, me.name
. . . see if that leads anywhere.
No, doh, look again, the clsEvents exposes a write only property UsrFrm(). Guess what that is for?
Cheers,
 
Hi

I had tried to pass the form directly into the class and I created a new property UsrFrm which in my post above.

So I pass it into the class like this

Code:
Set newCmd = New clsEvents
  Set newCmd.UsrFrm = Me
  col.Add newCmd, Me.Name

and in the class ive done this

Code:
Public Property Set UsrFrm(c As Object)
    
  Set ParentForm = c
  Set C15 = c

  Set myFrm = c
  c.OnClick = "[Event Procedure]"
  c.OnMouseMove = "[Event Procedure]"
  
End Property

This is nearly working...

If the cursor moves over the left of the form where the record selectors are it it trigger the event but not when the cursor is over the actual form (the Detail part).

Thanks
 
If we clear out all the stuff not need for the problem we should get to something like this.

Behind the Form:-
Code:
Option Compare Database
Option Explicit

Private col    As New Collection
Private newCmd As New clsEvents


Private Sub Form_Load()
    Dim ctl As Control
    
    ' Do the Form and Section(s)
    Set newCmd = New clsEvents
    Set newCmd.UsrFrm = Me
    col.Add newCmd, CStr(Me.Hwnd)
    
    For Each ctl In Me
        ' Do the other Controls.
    Next ctl

End Sub

And in the user defined class module:-
Code:
Option Compare Database
Option Explicit

Private WithEvents SectionDetail As Section
Private WithEvents Frm           As Form


Public Property Set UsrFrm(ThisForm As Form)
    
    Set Frm = ThisForm
    Frm.OnMouseMove = "[Event Procedure]"
  
    Set SectionDetail = Frm.Section(acDetail)
    SectionDetail.OnMouseMove = "[Event Procedure]"
    
End Property


Private Sub Frm_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

    ' Handle the Form MouseMove
    
End Sub


Private Sub SectionDetail_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

    ' Handle the Detail Section MouseMove
    
End Sub

Plenty of different ways to write it, for example if you want to set the handlers for all sections of the Form even if they don’t exist.
 
Hi Chris

Thanks for this.. I've got that working now...:):cool:

Last question on this I think.

I wanted reduce the amount of times the event is called and prevent unnecessary updating of controls.

In the section detail I've sorted it (I think this is the right way)
Code:
Private Sub SectionDetail_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
  ' Handle the Detail Section MouseMove
  Frm.Label1 = "You're over: " & SectionDetail.Name
  Frm.Section(acDetail).OnMouseMove = ""
  'strThisCtrl = ""
End Sub

Basically when you go over the detail section it turns off the mouse move event.

Then when you go over a control it turns it back on. This seems to be ok.
Code:
Private Sub C3_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
  ' Handle the rectangle MouseMove
  'If strThisCtrl = myObj.Name Then Exit Sub

  Frm.Label1 = "You're over: " & myObj.Name
  Frm.Section(acDetail).OnMouseMove = "[Event Procedure]"
  'strThisCtrl = myObj.Name

End Sub

The bit I can't get to work is preventing necessary updating of the same control so basically I want to ignore the mouse event if the control has already been triggered.

I though to declare a variable at the top of the module

Code:
Dim strThisCtrl As String

Then at the end of the mouse move assign the name of the control to the activecontrol.

Code:
strThisCtrl = myObj.Name

Then when the move event is triggered exit the sub if the control name is equal to the variable.

Code:
If strThisCtrl = myObj.Name Then Exit Sub

I've left the code in my post above but commented out.

What I don't understand is that this only works on the first mouse over of each control. After that the variable always matches whatever the active control is.

How is the name being assigned to the variable when it doesn't reach that line of the code? :banghead:

Hope this makes sense.:confused:

Really appreciate your help with this.
 
I've got this code (which I can't remember where from now, but I don't claim to have done it).

When you find some code online it is usually a good idea to paste the url into a comment at the top of your code. That way you can get back there later.
 
I do not believe it would prove fruitful to try and stuff all the code into one class module.

A common variable needs to be accessible from each instance of each Class. That variable could be a Public/Global variable or it could be a variable behind the Form. With this code I’ve gone for a variable behind the Form.


Behind the Form:-
Code:
Option Compare Text
Option Explicit

Public Obj  As Object           ' This is a pointer to the Object which may have
                                ' had its events turned off. It is back referenced
                                ' only from the user defined Class modules.
Private Col As New Collection


Private Sub Form_Load()
    Dim Ctl    As Control
    Dim NewObj As Object
    
    ' Do the Form and Section(s)
    Set NewObj = New clsFrmEvents
    Set NewObj.UsrFrm = Me
    Col.Add NewObj, CStr(Me.Hwnd)
    
    ' Do the other Controls.
    For Each Ctl In Me
        Select Case Ctl.ControlType
            Case acCommandButton
                Set NewObj = New clsCmdEvents
                Set NewObj.FrmCtl = Ctl
                Col.Add NewObj, CStr(ObjPtr(Ctl))
                
            Case acTextBox
                Set NewObj = New clsTxtEvents
                Set NewObj.FrmCtl = Ctl
                Col.Add NewObj, CStr(ObjPtr(Ctl))
                
        End Select
    Next Ctl

End Sub

The Form Class:-
Code:
Option Compare Text
Option Explicit

Private WithEvents SectionDetail As Section
Private WithEvents Frm           As Form


Public Property Set UsrFrm(ThisForm As Form)
    
    Set Frm = ThisForm
    Frm.OnMouseMove = "[Event Procedure]"
  
    Set SectionDetail = Frm.Section(acDetail)
    SectionDetail.OnMouseMove = "[Event Procedure]"
    
End Property


Private Sub Frm_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
    ' Handle the Form MouseMove
    TurnOnEventProcedure
    Frm.OnMouseMove = ""
    Set Frm.Obj = Frm
    
End Sub


Private Sub SectionDetail_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)

    ' Handle the Detail Section MouseMove
    TurnOnEventProcedure
    SectionDetail.OnMouseMove = ""
    Set Frm.Obj = SectionDetail
    
End Sub


Private Sub TurnOnEventProcedure()

    If Not Frm.Obj Is Nothing Then
        Frm.Obj.OnMouseMove = "[Event Procedure]"
    End If
    
End Sub

The Command Button Class:-
Code:
Option Compare Text
Option Explicit

Private WithEvents Cmd As CommandButton


Public Property Set FrmCtl(ThisControl As Control)
    
    Set Cmd = ThisControl
    Cmd.OnMouseMove = "[Event Procedure]"
    
End Property


Private Sub Cmd_MouseMove(Button As Integer, Shift As Integer, x As Single, Y As Single)
    Static XXX As Long
    
    If Not Cmd.Parent.Obj Is Nothing Then
        Cmd.Parent.Obj.OnMouseMove = "[Event Procedure]"
    End If
    
    Set Cmd.Parent.Obj = Cmd
    
    XXX = XXX + 1
    
    Cmd.Parent.Label1 = XXX
    Cmd.Parent.Section(acDetail).OnMouseMove = "[Event Procedure]"
    Cmd.OnMouseMove = ""

End Sub

The Text Box Class:-
Code:
Option Compare Text
Option Explicit

Private WithEvents Txt As TextBox


Public Property Set FrmCtl(ThisControl As Control)
    
    Set Txt = ThisControl
    Txt.OnMouseMove = "[Event Procedure]"
    
End Property


Private Sub Txt_MouseMove(Button As Integer, Shift As Integer, x As Single, Y As Single)
    Static XXX As Long
    
    If Not Txt.Parent.Obj Is Nothing Then
        Txt.Parent.Obj.OnMouseMove = "[Event Procedure]"
    End If
    
    Set Txt.Parent.Obj = Txt
    
    XXX = XXX + 1
    
    Txt.Parent.Label1 = XXX
    Txt.Parent.Section(acDetail).OnMouseMove = "[Event Procedure]"
    Txt.OnMouseMove = ""

End Sub

Attached is an Access2003 version of the above. You should notice that only the first mouse move event increments that Control’s counter.

If you have any questions, rather than posting back code, could you modify the attachment and post it back.

Chris.
 

Attachments

When you find some code online it is usually a good idea to paste the url into a comment at the top of your code. That way you can get back there later.

Hi, good tip...

The annoying this was I was sure I asked a question about it a couple of years about it but couldn't find anything..
 
I do not believe it would prove fruitful to try and stuff all the code into one class module.

A common variable needs to be accessible from each instance of each Class. That variable could be a Public/Global variable or it could be a variable behind the Form. With this code I’ve gone for a variable behind the Form.

Attached is an Access2003 version of the above. You should notice that only the first mouse move event increments that Control’s counter.

If you have any questions, rather than posting back code, could you modify the attachment and post it back.

Chris.

This is excellent... so clean and efficient.

Thanks for taking the time to help with this. A massive help and I'm sure lots of people will find it useful.

Thanks again

Kev

:D :D :D
 
Couldn't resist, took a stab at it.
 

Attachments

G’day Mark.

Common event handlers are reasonably easy to set up but I have always had trouble passing the system arguments.
For example the Mouse Move event may require:-
Button As Integer, Shift As Integer, X As Single, Y As Single
being passed and returned from/to the system.

This then seems to require a Class module for each type of control. I don’t know if you have ever seen a way to do it through a common handler.

Cheers,
Chris.
 
Hey Chris.
Yeah, if you needed that data from the event signature I believe you would need to use a WithEvents variable for each control. It's too bad the Access.Control object, from which these other objects must derive, doesn't raise events.
Cheers,
 

Users who are viewing this thread

Back
Top Bottom