Calling private sub from module, concatenated reference - vba

I have a couple of forms (i.e. frmTest) with bound comboboxes (i.e. cboTest). I'm trying to solve the NotInList event by a public sub, which calls back a button click sub's in these forms (i.e. btnTest_Click).
Form frmTest:
Private Sub cboTest_NotInList(NewData As String, Response As Integer)
Response = acDataErrContinue
Item_NotInList NewData, Me, "btnTest"
End Sub
Public Sub btnTest_Click
'....
End sub
Module:
Public strNotInList_Text As String
'public variable to store entered text
Public Sub Item_NotInList (strNewData As string, frmForm As Form, strControl As String)
Dim strControl_Sub As String
strNotInList_Text = strNewData
strControl_Sub = "." & strControl & "_Click"
Application.Run frmForm.Name & strControl_Sub
End Sub
Acces returns an error "Program ... didn't found a procedure frmTest.btnTest_Click."
Why ?
Reference frmTest.btnTest_Click looks to be correct. Sub btnTest_Click is declared as public.
Thank you for yor help.

Couldn't you create an interface let say IMyInterface with this method btnTest_Click (should be named differently) and let the forms you want to call this method implement this interface. Then change the signature of Item_NotInList like this:
Public Sub Item_NotInList (strNewData As string, frmForm As IMyInterface, strControl As String)
' ...
frmForm.btnTest_Click
' ...
Because the instance of the form is available in the method Item_NotInList you simply call the target method. Does this help?
Example:
Add class module and name it e.g. IMyInterface (can be named according to your needs). Add empty body of the method (don't add any implementation).
IMyInterface
Public Sub TestClick()
' will be implemented in your forms
End Sub
Then implement this interface in your forms e.g. in Form frmTest and others which should be used with the Item_NotInList method.
Form frmTest example
Implements IMyInterface
Private Sub IMyInterface_TestClick()
' here goes your implementation
End Sub
Standard Module test code
Sub test()
Dim f1 As UserForm1
Set f1 = New UserForm1
Item_NotInList f1
Dim f2 As UserForm2
Set f2 = New UserForm2
Item_NotInList f2
End Sub
Sub Item_NotInList(testForm As IMyInterface)
testForm.TestClick
End Sub
Thats it. HTH

Related

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

Calling functions outside class

what is the correct way to call function outside the class, below code is also working
tried an alternate way by using delegate but couldn't figure out to pass function with parameters from Form class to classname so that form function can be called.
Public class form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
dim cl as new classname
cl.run()
end sub
function testmsg(txt as string)
msgbox(txt)
end function
end class
public class classname
public sub run()
txt = "xyz"
if(condition = true) then call form1.testmsg(byref txt as string)
end sub
end class
You can at any time, and in any place, and from a form, a class object, or even just standard vb module call/use a function (or in your case, it should be a sub).
Just mark any routine (sub/function) in the form as public.
eg:
Public function testmsg(txt as string)
msgbox(txt)
end function
NOw, in any other form, class or in fact any place you have code, you can thus go:
FormA.TestMsg("hi from form B")
so, there are no restrictions here. And any form wide scoped variables marked as public can also be used:
eg:
Public Class FormA
Public Zoo As String
Public Function TestMsg(txt As String)
MsgBox(txt)
End Function
Public Sub ShowZoo()
MsgBox("value of zoo = " & Zoo)
End Sub
End Class
So above is FormA
Now, any code from any ohter form, class or whatever can do this:
FormA.TestMsg("hi from form B")
FormA.Zoo = "this is zoo value"
FormA.ShowZoo()
So, just mark any variable as public (it in effect becomes a public property of that form).
And, just mark any function/Sub as pubic (it in effect becomes a public "method" of that form.

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.

Is there a way to make a setable function in a user defined object

Is there a way to set an object method like a property?
I want to be able to just call the function like myObj.myFunc() but I want to set where myFunc will point when i instantiate myObj. As of right now i have it as a public event that i can add a handler to at init but it does seem like the best option.
As #VisualVincent said:
Module Module1
Sub Main()
Dim myObj As New myObj(Sub() Console.WriteLine("1"))
' OR
Dim myObj2 As New myObj(AddressOf myFunc)
myObj2.myFunc.Invoke
End Sub
Sub myFunc()
Console.WriteLine("2")
End Sub
End Module
Class myObj
Public Sub New(myFunc As Action)
Me.myFunc = myFunc
End Sub
Property myFunc As Action
End Class

Elegant pattern for multiple constructors

Assuming an object with the following code...
Public Sub New()
Me.Name = "Default Name"
Initialize()
End Sub
Public Sub New(CustomName as String)
Me.Name = CustomName
Initialize()
End Sub
Private Sub Initialize()
'Initialize some other properties
End Sub
Is there a more elegant pattern for this use case? Some way where one constructor could call the other constructor and eliminate the need for the Initialize() method?
Yes, you could write one constructor with all the needed parameters and then write the rest with a call to Me.Constructor() without anything else in the method block that supplies the defaults.
Public Sub New(CustomName as String)
Me.Name = CustomName
End Sub
Public Sub New()
Me.New("Default Name")
End Sub
I'm not sure if there's a VB syntax for constructor chaining in the C# sense, but if I remember correctly VB can invoke other constructors internally by calling Me.New(). Which is kind of the same thing. So you should be able to do something like this:
Public Sub New()
Me.New("Default Name")
End Sub
Public Sub New(CustomName as String)
Me.Name = CustomName
'Initialize some other properties
End Sub