1

I have this UserForm (Image 1) and I'm trying to apply some customization through Class Module. So, my first goal was to modify the label format when it was clicked (Image 2). So far so good, I've accomplished this through the Class Module "cLabels". Now, my second goal is (this is the one I'm stuck) to apply some other color to the aforementioned Label. The point is, I don't know how to accomplish this.

I tried to create other class module called "cUserForm", but I don't how to pass the label modified to the cUserForm Class Module and use its MouseMove Event. I know I could apply the modification through the standard UserForm Module using the MouseMove Event, but the thing is, I don't want any code like that in my UserForm Module, I want the class module doing the "dirty" work. Do guys have any ideas how can I circumvent the problem?

Additional information (but not important to solve the problem): My final goal is to make "Buttons" like this https://drive.google.com/file/d/1ev_LNgxPqjMv0dtzlF7GSo7SOq0wDbR2/view?usp=sharing with some effects such as MouseHover, TabPress and so on. VBA buttons are very ugly. Just for the record, I've already done all this in a standard UserForm module (If anyone wants the workbook to see what I'm talking about, I have it), but the final result was just a mess, so many code (and It was just the code to modify the appearance of the UserForm, imagine when I put some code to do certain action, omg).

Image 1

Image 2

Here is what I have so far:

UserForm Module

Option Explicit

Private ObjLabel As cLabels
Private ObjUserForm As cUserForm

Private Sub UserForm_Initialize()

 Set ObjLabel = New cLabels
 ObjLabel.CallClasse Me
 
 Set ObjUserForm = New cUserForm
 Set ObjUserForm.UserFormValue = Me
 
End Sub

cLabels

Option Explicit

'## Events/Variables/Collections
Private WithEvents clsLabel As MSForms.Label

Private ClasseObject As cLabels
Private LabelCollection As New Collection

'## Properties
Public Property Get ActiveLabel() As MSForms.Label
    Set ActiveLabel = clsLabel
End Property

Public Property Set ActiveLabel(Value As MSForms.Label)
    Set clsLabel = Value
End Property

'## Procedures/Methods
Private Sub clsLabel_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
 LabelHovered
End Sub

Public Sub CallClasse(MainObject As MSForms.UserForm)

 Dim ctrl As MSForms.Control

 For Each ctrl In MainObject.Controls

    If TypeOf ctrl Is MSForms.Label Then
        Set ClasseObject = New cLabels
        Set ClasseObject.ActiveLabel = ctrl
        LabelCollection.Add ClasseObject
    End If

 Next ctrl

End Sub

Private Sub LabelHovered()
 ActiveLabel.BackColor = vbYellow
End Sub

cUserForm

Option Explicit

'## Events/Variables/Collections
Private WithEvents clsUserForm As MSForms.UserForm
Private mActiveLabel As MSForms.Label
Private ObjLabel As New cLabels

'## Properties
Public Property Get UserFormValue() As MSForms.UserForm
    Set UserFormValue = clsUserForm
End Property

Public Property Set UserFormValue(Value As MSForms.UserForm)
    Set clsUserForm = Value
End Property

Public Property Get ActiveLabel() As MSForms.Label
    Set ActiveLabel = mActiveLabel
End Property

Public Property Set ActiveLabel(Value As MSForms.Label)
    Set mActiveLabel = Value
End Property

'## Procedures
Private Sub clsUserForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    'MsgBox ObjLabel.ActiveLabel.BackColor 'Got an error
End Sub

Workbook: https://drive.google.com/file/d/1cLG4pLmC-jDaysjd_dK0EFuJ_LqYqJ-u/view?usp=sharing

Ninja
  • 15
  • 1
  • 6
  • Hi, I'm not clear on what you mean by "Apply some other color". You seem to have working a label that will change background color to yellow when you hover over it. Are you saying you want to change that to a different color? – ArcherBird Jul 23 '20 at 15:20
  • Are you trying to make a label that highlights when moused over and then "unhighlights" when the mouse leaves the label? – ArcherBird Jul 23 '20 at 15:23
  • @ ArcherBird Yeah, I want this "unhighlight" effect. This unhighlight effect would be some standard color, let's say grey for example, and when the label is hovered the color must change, and so on. I could go on the UserForm Module and use a loop to set the standard color for the labels, but I don't know it's seems a cheap solution. I don't know how, but whenever I click on a label, the class module cLabels save this label clicked and change only its color. If the label clicked is saved, I would be able to pass it for another class module? – Ninja Jul 23 '20 at 15:49

2 Answers2

0

You don't need to create a separate class module to change things in the form. Just add event-handling methods in the code behind for the form. (In the form editor, right click on the form and select "View code".)

You can use the MouseMove event for the button to change its colour, and then use the MouseMove event for the form to reset the button colour, like so:

Private Sub UserForm_MouseMove(ByVal Button As Integer, _
    ByVal Shift As Integer, _
    ByVal X As Single, _
    ByVal Y As Single)

    CommandButton1.BackColor = &H8000000F
End Sub

Private Sub CommandButton1_MouseMove(ByVal Button As Integer, _
    ByVal Shift As Integer, _
    ByVal X As Single, _
    ByVal Y As Single)

    CommandButton1.BackColor = vbYellow
End Sub
Peter Constable
  • 2,707
  • 10
  • 23
  • Thanks for the reply, but you've missed the point. I didn't really want to do this with CommandButton because I think it is ugly, I wanted to use Labels. The idea of a Class Module was to hide all this "Appearance" code. Take a look on this workbook where I've done all the effects that I want in my final project with Class Module but it's in UserForm module. https://drive.google.com/file/d/1BCvp9J6cMkvrG-SJl_zPu_aPT4HBxXxN/view?usp=sharing – Ninja Jul 23 '20 at 16:51
0

I found your question very interesting and I've got a bit of a different, more object oriented take on how you might do this. I tried implementing an Observer Pattern to get the described effect. (As a side note, normally I would generalize a solution a bit more using Interfaces, but for this quick demo, I will show a couple of tightly coupled classes that get the job done)

Allow me to first introduce all my components:

Classes:

LabelObserver

Option Explicit

Private WithEvents mInteralObj As MSForms.label
Private mBackGroundColor As Long
Private mMouseOverColor As Long

Private Const clGREY As Long = &H8000000F

'// "Constructor"
Public Sub Init(label As MSForms.label, _
                Optional mouseOverColor As Long = clGREY, _
                Optional backGroundColor As Long = clGREY)
                
    Set mInteralObj = label
    mBackGroundColor = backGroundColor
    mMouseOverColor = mouseOverColor
End Sub

Private Sub Class_Terminate()
    Set mInteralObj = Nothing
End Sub

Public Sub MouseLeft()
    '//Remove Highlight
    mInteralObj.BackColor = mBackGroundColor
End Sub

Private Sub mInteralObj_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    '//Highlight
    mInteralObj.BackColor = mMouseOverColor
End Sub

LabelNotifier

Option Explicit
Private observersCollection As Collection

Private Sub Class_Initialize()
    Set observersCollection = New Collection
End Sub

Public Sub AddObserver(observer As LabelObserver)
    observersCollection.Add observer
End Sub

Public Sub RemoveObserver(observer As LabelObserver)
    Dim i As Long
    '// We have to search through the collection to find the observer to remove
    For i = 1 To observersCollection.Count
        If observersCollection(i) Is observer Then
            observersCollection.Remove i
            Exit Sub
        End If
    Next i
End Sub

Public Function ObserverCount() As Integer
    ObserverCount = observersCollection.Count
End Function

Public Sub Notify()
    Dim obs As LabelObserver
    
    If Me.ObserverCount > 0 Then
    
        For Each obs In observersCollection
            '//call each observer's MouseLeft method
            obs.MouseLeft
        Next obs
    
    End If
End Sub

Private Sub Class_Terminate()
    Set observersCollection = Nothing
End Sub

Module:

LabelObserverFactory (this is kinda optional - it simply provides a nice streamlined way of creating valid LabelObservers)

Option Explicit

Public Function NewYellowHighlightCustomLabel(label As MSForms.label) As LabelObserver
    Dim product As New LabelObserver
    
    product.Init label, vbYellow
    
    Set NewYellowHighlightCustomLabel = product
End Function

Public Function NewRedHighlightCustomLabel(label As MSForms.label) As LabelObserver
    Dim product As New LabelObserver
    
    product.Init label, vbRed
    
    Set NewRedHighlightCustomLabel = product
End Function

UserForm

MyForm (note that this form has three labels with default names placed on it for the purposes of this demo)

Option Explicit

Private notifier As LabelNotifier


Private Sub UserForm_Initialize()
    Set notifier = New LabelNotifier
    
    '//add controls to be notified
    notifier.AddObserver LabelObserverFactory.NewYellowHighlightCustomLabel(Me.Label1)
    notifier.AddObserver LabelObserverFactory.NewRedHighlightCustomLabel(Me.Label2)
    notifier.AddObserver LabelObserverFactory.NewYellowHighlightCustomLabel(Me.Label3)
    
    
End Sub

Private Sub UserForm_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    '//Notify labels that mouse has left them
    notifier.Notify
End Sub

Private Sub UserForm_Terminate()
    Set notifier = Nothing
End Sub

Now, to explain what's going on here:

The form has a LabelNotifier object, which gets established when the form initializes, that it will use to notify our labels that the mouse has moved away from them. We do this by listening for the form's MouseMove event. (I know you are trying to avoid using this, but hopefully the fact that ours will just have one line of code, no matter how many labels you are impacting, will satisfy the desire to encapsulate logic elsewhere.) When we get a mouse move, we will have the notifier do its only job, to send a message to all the labels we added to it.

The LabelObserver is the counter part of the LabelNotifier. A label observer is responsible for telling the labels to change color and which colors to use.

Even if you don't like this implementation, I had fun making it. :-)

ArcherBird
  • 2,019
  • 10
  • 35
  • I can also show you how you might add click events to these labels, if you are trying to use them to build-your-own buttons – ArcherBird Jul 23 '20 at 19:04
  • Wow thank very much for the help brother. I'll need some time to go over your code (working very well by the way) and understand what you've done, but you already gave me some ideas that I'll try on my project. Again, thanks for the attention and help. (I tried to give "This answer is useful" a plus but my reputation is under the minimum for it). – Ninja Jul 23 '20 at 20:35
  • @EdsonMatheus no problem friend. Even though vba lacks many features of other modern languages, for some reason I love the challenge of designing in it. Let me know if you have questions! :-) – ArcherBird Jul 23 '20 at 20:47
  • 1
    your code solved my problem, I was having issues with variables out of scope but somehow your code circumvent that and I think it is because of the "Constructor" procedure, it seems this procedure kind of "Convert" the label into a class module object (although I’m not sure about that) and then you add it into a collection, so in this way the collection don't go out scope. I made some changes to the code and changed the variables/class modules names in order to it become more understandable. I couldn't be more grateful. Now my buttons are more attractive :) – Ninja Jul 29 '20 at 15:17
  • @EdsonMatheus Glad it worked out for you! It was a fun problem to play with :-) – ArcherBird Jul 29 '20 at 15:23