Is there a way to make a setable function in a user defined object - vb.net

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

Related

Access VBA tries to terminate return value of Function

I'm building an Access database using some VBA code.
I've made a very simple class and a function which creates an instance of that class. Executing this function yields an error 91: object variable or With block variable not set. After debugging I found out Access tries to terminate the newly created object, ending the terminate function results in the error.
My intention was to create a factory for MyObject in order to create an object and initialize it with given arguments. I've stripped all this away to find the problem and now I'm dumbfounded. Can someone point out what I'm doing wrong because I have no idea anymore.
The class I made, called MyObject:
Private Sub Class_Initialize()
'does literally nothing
End Sub
Private Sub Class_Terminate()
'does literally nothing
End Sub
The function I made to initialize the class, located in another module:
Public Function createMyObject(someArg As String) As MyObject
Set createMyObject = New MyObject
End Function
I obviously expected the function createMyObject to return an instance of MyObject, but it gives me an error 91. Debugging led me to the Class_Terminate Sub in MyObject where the error gets thrown at the "end sub" line.
Try with using a property:
Option Compare Database
Option Explicit
Private mTest As Variant
Private Sub Class_Initialize()
'does literally nothing
End Sub
Private Sub Class_Terminate()
'does literally nothing
End Sub
Public Property Get Test() As Variant
Test = mTest
End Property
Public Property Let Test(ByVal NewValue As Variant)
mTest = NewValue
End Property
and then:
Public Function CreateMyObject(SomeArg As String) As MyObject
Set CreateMyObject = New MyObject
CreateMyObject.Test = SomeArg
End Function
which you can call like this:
Set o = CreateMyObject("Joe")
Debug.Print o.Test

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.

Calling private sub from module, concatenated reference

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

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

VBA: evaluation order

when VBA executes this line:
GetClass1().Test(GetParam())
the GetParam function is evaluated before the GetClass1() call.
What is a good way to change this behaviour?
the only thing I came up with is this workaround:
With GetClass1
.Test(GetParam())
End With
here's the full example code, so that you can easily test it:
Class1
Option Explicit
Public Function Test(ByVal sText As String) As String
Debug.Print "Class1.Text: " & sText
Test = "Class1.Text: " & sText
End Function
Module1
Option Explicit
Private Function GetClass1() As Class1
Set GetClass1 = New Class1
Debug.Print "GetClass1()"
End Function
Private Function GetParam() As String
GetParam = "Param"
Debug.Print "GetParam()"
End Function
Private Sub Test()
Debug.Print "Test=" + GetClass1().Test(GetParam())
With GetClass1
Debug.Print "TestWith=" + .Test(GetParam())
End With
End Sub
Output when you run Test()
GetParam()
GetClass1()
Class1.Text: Param
Test=Class1.Text: Param
GetClass1()
GetParam()
Class1.Text: Param
TestWith=Class1.Text: Param
The evaluation order here is ok i think. The calling order of nested functions is from the inner most one to the outer most one which can't be done differently because outer most function needs to know its arguments and this arguments are evalueted only after the inner function was executed.
In your code (the first way) the object of type Class1 is created after the function GetParam() was called and this is because the object is created at the moment when function GetClass1() is called. In the second way with With GetClass1 the object is created immedialtelly after With and the call stack looks differently indeed.
What you can do is to create another class say 'Wrap' and this class will be responsible for creation of instance of type Class1.
E.g. like this:
' Class module Wrap
Private m_class1 As Class1
Public Function GetClass1() As Class1
Set GetClass1 = m_class1
Debug.Print "GetClass1()"
End Function
Private Sub Class_Initialize()
Set m_class1 = New Class1
End Sub
' Module code
Private Function GetParam() As String
GetParam = "Param"
Debug.Print "GetParam()"
End Function
Private Sub Test()
Dim wp As Wrap
Set wp = New Wrap
Debug.Print "Test=" + wp.GetClass1().Test(GetParam())
Debug.Print "---------------------------------------"
With New Wrap
Debug.Print "TestWith=" + .GetClass1.Test(GetParam())
End With
End Sub
Here the instance of class Class1 is created exactly at the moment you call New for class Wrap. So Set wp = New Wrap executes and creates the instance and the same way works With New Wrap, it executes and creates the instance as well.
But do not do it like this:
Dim wp As New Wrap
... then you will have the same behaviour like you had when the function GetClass1() was part of the Module1, which is: 'the instance is not created until it is needed' so you do not have the control of the moment of creation. HTH