How to override class method in VBA? - vba

I have the following three class modules:
interface:
Private Sub subCall()
End Sub
Public Sub mainCall()
End Sub
parent:
Implements interface
Private Sub interface_subCall()
Debug.Print "sub call"
End Sub
Public Sub interface_mainCall()
interface_subCall
Debug.Print "main call"
End Sub
child:
Implements interface
Public p As New parent
Private Sub interface_subCall()
Debug.Print "child sub call"
End Sub
Public Sub interface_mainCall()
p.interface_mainCall
End Sub
I want to inherite child from parent and override interface_subCall() so that when I call interface_mainCall() - inherited method called (not parent).
I tried this code:
Dim c As New child
c.interface_mainCall
But it is calling parent's interface_subCall - printing "sub call" rather than "child sub call".
As far as I understand - there is no inheritance in VBA. If so - could you please advise - how to do it with composition.

I think the following approach is the best.
child class module:
Public Sub subCall()
Debug.Print "child sub call"
End Sub
parent class module:
Public Sub subCall()
Debug.Print "sub call"
End Sub
main class module:
Public Sub mainCall(child)
child.subCall
End Sub
Now I can pass child/parent as argument to main:
Dim c As New child
Dim m As New main
m.mainCall c
Dim p As New parent
m.mainCall p
So I can avoid duplication of main code.

Related

Find out from within a class if an event handler exists in another class

A VB.NET 4 question.
Let's assume there exists a class A which contains an event E. In another Class (B), a variable of type A is declared WithEvents. At a certain point withing A's code, there will be a "RaiseEvent E" command. After that, is there a way to know if E was handled within B (just if a handler of event E exists in Class B)?
Obviously E could contain a parameter (i.e. boolean) so that if a handler within B handles it, it could set this parameter to True. This is not what i'm asking though. I would like to know if there is a built-in-to-.NET way to achieve this, without the use of any parameters.
A code example of what i'm trying to avoid (the use of parameter DoSomethingWasHandled):
Public Class A
Public Event DoSomething(ByRef DoSomethingWasHandled As Boolean)
Public Sub RaiseDoSomething()
Dim DoSomethingWasHandled As Boolean = False
RaiseEvent DoSomething(DoSomethingWasHandled)
End Sub
End Class
Public Class B
Public WithEvents SomeA As New A
Private Sub HandleDoSomething(ByRef DoSomethingWasHandled As Boolean) Handles SomeA.DoSomething
DoSomethingWasHandled = True
End Sub
End Class
A code example of what i'm asking if it exists:
Public Class A
Public Event DoSomething()
Public Sub RaiseDoSomething()
RaiseEvent DoSomething()
If DoSomething.WasHandled Then '<-- Does this check exist in any form? ***
DoSomethingElse()
End If
End Sub
End Class
Public Class B
Public WithEvents SomeA As New A
Private Sub HandleDoSomething() Handles SomeA.DoSomething
'DoStuff...
End Sub
End Class
*** This would just check if an event handler of E exists in class B and return True if it does, False if it doesn't.
Like this.
Public Class A
Public Event DoSomething(e As Object)
Public Sub RaiseDoSomething()
' adding 'Event' to the variable name of
' the event allows it to be checked
If DoSomethingEvent IsNot Nothing Then 'does the event have a subscriber(Event Handler)?
'yes
RaiseEvent DoSomething("TEST") 'because there is a handler this will be handled
End If
End Sub
End Class
Public Class B
Public WithEvents SomeA As New A
Private Sub HandleDoSomething(e As Object) Handles SomeA.DoSomething
Debug.WriteLine("Do")
End Sub
End Class
A test
Dim FOOa As New A
Dim fooB As New B
FOOa.RaiseDoSomething()
fooB.SomeA.RaiseDoSomething()

Multi-Threading (Calling Sub From WorkerThread)

Need to call a sub that is coded written inside the block of form1 form an external worker thread. This is what I have written:
In Form1:
Public Delegate Sub UpdateControlDelegate(ByVal C As Label, ByVal txt As String)
Private Sub UpdateControl(ByVal C As Label, ByVal txt As String)
If C.InvokeRequired Then
C.Invoke(New UpdateControlDelegate(AddressOf UpdateControl), New Object() {C, txt})
Else
C.Text = txt
End If
End Sub
Public Sub DoStuff()
'we do some stuff then when it comnes time update a certain control:
Call UpdateControl(MyLabel, "My Text For The Label)
End Sub
In The workerThread that is located in a class:
Public Class MyClass
Public Sub UpdateData
Call Form1.DoStuff
End Sub
End Class
Does this look correct? The most simplest terms on what I am trying to achieve:
WorkerThread to call a Sub that is located in Class Form1
and that sub contains code that updates a couple controls in Form1.
After doing a little more research. I have figured it out. The initial code I have written is correct. The only thing missing is a reference to the form I need to update.
Here is the COMPLETE solution when needing to run a SUB from the UI that is called from the Worker Thread:
Public Class MyClass
'working thread is being within the subs of this class
Public MyForm1111 As Form1 '<------ The variable in this class that will reference to the form1 that we need
Public Sub MySubThatIsOnAWorkerThread
MyForm1111.DoStuff '<==== must call MyForm1111.DoStuff and NOT Form1.DoStuff
End Sub
End Class
The Sub Located In Form1:
Public Class Form1
Public Delegate Sub UpdateControlDelegate(ByVal C As Label, ByVal txt As String) 'Required Delegate
Private Sub UpdateControl(ByVal C As Label, ByVal txt As String) 'Sub to update controls
If C.InvokeRequired Then
C.Invoke(New UpdateControlDelegate(AddressOf UpdateControl), New Object() {C, txt})
Else
C.Text = txt
End If
End Sub
Public Sub DoStuff() 'the sub we need to call from the worker thread
'do some calculations and code
Call UpdateControl(MyLabel, "Some Text For Label")
End Sub
Private Sub Form1_Load()
MyClass.MyForm1111 = Me <==== Set the reference here in your Form1_Load
End Sub
End Class

VBA get instance name inside of class

Sub Main()
Dim test As New Class1
End sub
Class1:
Private Sub Class_Initialize()
msgbox(Name_of_Class_Instance)
End Sub
I want the msgbox to show "Test"
Can that be done in VBA?
As per my understanding, you are trying to get the class instance name which is declared in module.
First of all you need to do is create a class like below.
Public classname As String
'Below method is going to get the value from module.
Public Sub Class_Initialize()
MsgBox classname
End Sub
Second, create a module like below.
Sub getname()
Dim test As Class1
Set test = New Class1
'Here we are passing the class name as 'test' and executing the 'Class_Initialize' method
With test
.classname = "test"
.Class_Initialize
End With
End Sub
Now you will get the instance name of the class.

Access variable in Shared Sub

is there a way to access a variable in Form_Load from an event handler?
Please dont mind the code, this is just a representation of my question.
Public Class Form
Public Sub Form_Load()
Dim x as string
x = MyClass.MethodGetValue()
End Sub
Private Shared Sub OnChanged()
MyClass2.MethodGetValue(x)
End Sub
End Class
It's about the scope of the variable. In your situation you need a class variable. This allows it to be used anywhere inside of this class.
Public Class Form1
Private x As Object 'pick the datatype that matches your needs
Public Sub Form_Load()
x = MyClass.MethodGetValue()
End Sub
Private Sub OnChanged()
MyClass2.MethodGetValue(x)
End Sub
End Class

referencing form button from another form

I have two forms both with the same buttons on, and I want to have it so that if I click the button both buttons will do the same thing i.e. they are referencing each other on different forms. the way i found was:
Public Class Form2
Dim form1 As New form1
Private Sub Button2_Click
form1.backcolor=black
form2.backcolor=black
end sub
end class
then
Public Class Form1
Dim form2 As New form2
Private Sub Button1_Click
form1.backcolor=black
form2.backcolor=black
end sub
end class
only this doesn't work as there is an error:An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll as far as i can see there is no infinite loop or stack over flow.
any help would be greatly appreciated.
You have an infinite loop, because each time one of the forms is instantiated, it is instantiating the other. Creating a Form1 will create a Form2, then Form2 immediately creates another Form1 and so on and so on...
Change your code to this:
Public Class Form2
Private Sub Button2_Click
Dim form1 As New Form1
form1.backcolor=black
form2.backcolor=black
End sub
End class
Public Class Form1
Private Sub Button1_Click
form1.backcolor=black
Dim form2 As New Form2
form2.backcolor=black
End sub
End class
Now it will only create the other class instances when you click a button.
Like Karl Anderson said, there is a infinite loop in your code. His solution will create a new form every time you click the button. If you don't want this behavior, I think that the best approach is to use the mediator pattern. And it will be much more easy if you want to add new actions and new forms.
The code will look something like this:
Public Class Mediator
Private forms As New List(Of BaseForm)
Public Sub RegisterForm(form As BaseForm)
forms.Add(form)
End Sub
Public Sub ChangeAllFormsBackColorToBlack()
For Each form In forms
form.ChangeBackColorToBlack()
Next
End Sub
End Class
Public Class BaseForm
Private med As Mediator
Public Sub New(med As Mediator)
Me.med = med
Me.med.RegisterForm(Me)
End Sub
Public Sub ChangeBackColorToBlack()
backcolor = black
End Sub
Public Sub OnButtonClick()
Me.med.ChangeAllFormsBackColorToBlack()
End Sub
End Class
Public Class Form2
Inherits BaseForm
Public Sub New(med As Mediator)
MyBase.New(med)
End Sub
Private Sub Button2_Click()
Me.OnButtonClick()
End Sub
End Class
Public Class Form1
Inherits BaseForm
Public Sub New(med As Mediator)
MyBase.New(med)
End Sub
Private Sub Button1_Click()
Me.OnButtonClick()
End Sub
End Class
Module MediatorDemo
Sub Main()
Dim med As New Mediator
Dim f1 As New Form1(med)
Dim f2 As New Form2(med)
f1.OnButtonClick()
End Sub
End Module