Array of method calls using thread - multithreading

I am using Windows Form Application. In that, I have a class, which consists of 5 different methods. All of them are filling private members from various sources using thread.
For that I am using following code snippet to call a method
Dim threadForMethod1 As Threading.Thread
threadForMethod1 = New Threading.Thread(AddressOf Method1)
threadForMethod1.Start()
Now I want to add on / off switch for thread. By the way, If I off the switch all the methods should execute using main thread.
What is the best way to implement it.

You can use the following Class:
Imports System.Threading
Public Class ThreadHandler
Dim t1 As New Thread(AddressOf M1)
Dim t2 As New Thread(AddressOf M2)
Public Sub M1()
Thread.Sleep(3000)
MsgBox("M1")
End Sub
Public Sub M2()
Thread.Sleep(3000)
MsgBox("M2")
End Sub
Public Sub StartAll()
If t1.ThreadState <> ThreadState.Unstarted Then
t1 = New Thread(AddressOf M1)
End If
If t2.ThreadState <> ThreadState.Unstarted Then
t2 = New Thread(AddressOf M2)
End If
t1.Start()
t2.Start()
End Sub
Public Sub StopAll()
t1.Abort()
t2.Abort()
End Sub
End Class
To Use the above Class:
Private th As New ThreadHandler()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
th.StartAll()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
th.StopAll()
End Sub

Related

VB.net how to wait for user to select an Excel cell

I have created a VSTO project in Visual Studio with a Ribbon and a Form. There is a button on this form which I want when a user clicks on it, it waits for user to select a single cell from the workbook which this add-in is used in it.
I am currently at designing stage and have not much code written. Any key ideas to achieving this? Should I use Application.SendKeys method? Any other/better ideas?
Thanks
My Ribbon Code so far:
Imports Microsoft.Office.Tools.Ribbon
Public Class Ribbon1
Private Sub Ribbon1_Load(ByVal sender As System.Object, ByVal e
As RibbonUIEventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As Object, e As
RibbonControlEventArgs) Handles Button1.Click
Dim f As New Form1
f.Show()
End Sub
End Class
My Form Code so far:
Public Class Form1
'Variables for my "Custom Vlookup Function"
Dim RHeader As Excel.Range 'Reference Header
Dim SRange As Excel.Range 'Seleceted Range
Dim ColNo As Integer 'Column number ahead
Dim xlApp As Excel.Application = Globals.ThisAddIn.Application
Dim xlWB As Excel.Workbook =
Globals.ThisAddIn.Application.ActiveWorkbook
Private Sub Button2_Click(sender As Object, e As EventArgs)
Handles Button2.Click
End Sub
End Class
Wait for the Worksheet.SelectionChange event which is fired when the selection changes on a worksheet.

Setting (Private WithEvents As Sheet1) sheetUI = Sheet1 causes error 438: Object doesn't support this property or method

I am using this excellent tutorial as a base to create a simple "Hello World" Excel VBA project leveraging on Mathieu Guindon's concept of writing Object-Oriented Programming VBA code, discussed in a series of articles on the https://rubberduckvba.wordpress.com/ blog.
I have created a "bare bones" project without any Model containing an Excel worksheet (HelloSheet), a View, a ViewAdapter (including ViewCommands and ViewEvents interfaces) and a Controller. The VBA project compiles without errors but when I try to run the "application entry" macro I get the dreaded "Run-time error 438: Object doesn't support this property or method". This happens inside the Class_Initialize() sub of my View class where I have declared "Private WithEvents sheetUI As HelloSheet" and try to set "sheetUI = HelloSheet".
Here is an overview of my project tree, as seen in the RubberDuck VBIDE.
I have tried updating the VBA project references to exactly match those of the "Battleship" sample project. I also tried the two different approaches to implementing the Lazy Object / Weak Reference in the View class - the one in the "Battleship (WorksheetView).xlsm" linked in the original article vs the approach used in the latest version on GitHub, more specifically:
Private adapter As ***IWeakReference***
Private WithEvents sheetUI As HelloSheet
Private Sub Class_Initialize()
sheetUI = HelloSheet
End Sub
Private Property Get ViewEvents() As ISheetViewEvents
Set ViewEvents = adapter ***.Object***
End Property
VS
Private adapter As ***SheetViewAdapter***
Private WithEvents sheetUI As HelloSheet
Private Sub Class_Initialize()
sheetUI = HelloSheet
End Sub
Private Property Get ViewEvents() As ISheetViewEvents
Set ViewEvents = ***adapter***
End Property
..but the "Run-time error 438: Object doesn't support this property or method" persisted.
Below is all relevant code split in sheets, classes, interfaces etc.:
1) HelloSheet (regular Excel sheet code-behind):
'#Folder("HelloWorld.View.Worksheet")
Option Explicit
Public Event DoubleClick(ByVal clickedRow As Integer)
Public Sub HideShape(shapeName As String)
Dim currentShape As Shape
Set currentShape = Me.Shapes(shapeName)
currentShape.Visible = msoFalse
End Sub
Public Sub ShowShape(shapeName As String)
Dim currentShape As Shape
Set currentShape = Me.Shapes(shapeName)
currentShape.Visible = msoTrue
End Sub
Public Sub OnLaunchCommand()
ShowShape ("WarningTriangle")
End Sub
Public Sub TempManualHide()
HideShape ("WarningTriangle")
End Sub
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
RaiseEvent DoubleClick(Target.Row)
End Sub
Public Sub PreviewSelectedRecord(ByVal selectedRow As Integer)
Me.Cells(1, 1).Value2 = "Row is " & CStr(selectedRow)
End Sub
2) SheetView class:
'#Folder("HelloWorld.View.Worksheet")
Option Explicit
Implements ISheetViewCommands
Private adapter As SheetViewAdapter ' IWeakReference
Private WithEvents sheetUI As HelloSheet
Private Sub Class_Initialize()
sheetUI = HelloSheet
End Sub
Private Property Get ViewEvents() As ISheetViewEvents
Set ViewEvents = adapter '.Object
End Property
':GameSheet event handlers
':Messages sent from the view
':***************************
Private Sub sheetUI_DoubleClick(ByVal clickedRow As Integer)
ViewEvents.PreviewSelectedRecord clickedRow
End Sub
':IGridViewCommands
':Messages sent from the controller
':*********************************
Private Property Set ISheetViewCommands_Events(ByVal value As ISheetViewEvents)
Set adapter = value ' WeakReference.Create(Value)
End Property
Private Property Get ISheetViewCommands_Events() As ISheetViewEvents
Set ISheetViewCommands_Events = adapter '.Object
End Property
Private Sub ISheetViewCommands_OnLaunchCommand()
sheetUI.OnLaunchCommand
End Sub
Private Sub ISheetViewCommands_OnPreviewSelectedRecord(ByVal selectedRow As Integer)
sheetUI.PreviewSelectedRecord selectedRow
End Sub
3) ISheetViewEvents interface:
'#Folder("HelloWorld.View")
'#Interface
Option Explicit
Public Sub PreviewSelectedRecord(ByVal selectedRow As Integer)
End Sub
4) ISheetViewCommands interface:
'#Folder("HelloWorld.View")
'#Interface
Option Explicit
'#Description("Gets/sets a weak refererence to the view events.")
Public Property Get Events() As ISheetViewEvents
End Property
Public Property Set Events(ByVal value As ISheetViewEvents)
End Property
Public Sub OnLaunchCommand()
End Sub
Public Sub OnPreviewSelectedRecord(ByVal selectedRow As Integer)
End Sub
5) SheetViewAdapter class (PredeclaredId / has default instance):
'#Folder("HelloWorld.View")
Option Explicit
'#PredeclaredId
Implements ISheetViewCommands
Implements ISheetViewEvents
Public Event OnPreviewCurrentSelectedRecord(ByVal selectedRow As Integer)
Private Type TAdapter
SheetViewCommands As ISheetViewCommands
End Type
Private this As TAdapter
Public Function Create(ByVal view As ISheetViewCommands) As SheetViewAdapter
With New SheetViewAdapter
Set .SheetViewCommands = view
Set view.Events = .Self
Set Create = .Self
End With
End Function
Public Property Get Self() As SheetViewAdapter
Set Self = Me
End Property
'#Description("Gets/sets a reference that exposes commands to send to the view.")
Public Property Get SheetViewCommands() As ISheetViewCommands
Set SheetViewCommands = this.SheetViewCommands
End Property
Public Property Set SheetViewCommands(ByVal value As ISheetViewCommands)
Set this.SheetViewCommands = value
End Property
':IGridViewEvents
':Messages sent from the view
':***************************
Private Sub ISheetViewEvents_PreviewSelectedRecord(ByVal selectedRow As Integer)
RaiseEvent OnPreviewCurrentSelectedRecord(selectedRow)
End Sub
':IGridViewCommands
':Messages sent from the controller
':*********************************
Private Property Set ISheetViewCommands_Events(ByVal value As ISheetViewEvents)
Err.Raise 5, TypeName(Me), "Invalid use of property"
End Property
Private Property Get ISheetViewCommands_Events() As ISheetViewEvents
Set ISheetViewCommands_Events = Me
End Property
Private Sub ISheetViewCommands_OnLaunchCommand()
this.SheetViewCommands.OnLaunchCommand
End Sub
Private Sub ISheetViewCommands_OnPreviewSelectedRecord(ByVal selectedRow As Integer)
this.SheetViewCommands.OnPreviewSelectedRecord selectedRow
End Sub
6) HelloController class:
'#Folder("HelloWorld")
Option Explicit
Private viewCommands As ISheetViewCommands
Private WithEvents viewAdapter As SheetViewAdapter
Public Sub Launch(ByVal adapter As SheetViewAdapter)
Set viewAdapter = adapter
Set viewCommands = adapter
viewCommands.OnLaunchCommand
End Sub
Private Sub viewAdapter_OnPreviewCurrentSelectedRecord(ByVal selectedRow As Integer)
viewCommands.OnPreviewSelectedRecord selectedRow
End Sub
7) And finally the "Macros" standard module which serves as an entry point. This is where I encounter the error (the "Set view = New SheetView" line):
'#Folder("HelloWorld")
'#Description("Application entry points.")
Option Explicit
'#Ignore MoveFieldCloserToUsage
Private controller As HelloController
Public Sub LaunchWorksheetInterface()
Dim view As SheetView
Set view = New SheetView
Set controller = New HelloController
controller.Launch SheetViewAdapter.Create(view)
End Sub
Supposing I could get around the entry-level error, I would expect a very simple functionality:
1) A hidden Excel shape is made visible on the HelloSheet (the OnLaunchCommand);
2) When double-clicking on a cell, the row it is located on would be reported in cell A1 of the same worksheet (the Worksheet_BeforeDoubleClick event).
Obviously this amount of code for such simple tasks is overkill - my idea is once I get these basics working to add Model classes to the project and map them to certain areas (i.e. Tables/ListObjects) inside the Workbook.
Any help will be greatly appreciated! And kudos to anyone who has made it to the end of this rather long post :)
Private WithEvents sheetUI As HelloSheet
Private Sub Class_Initialize()
sheetUI = HelloSheet
End Sub
sheetUI is an object reference, assigning it requires the Set keyword:
Private WithEvents sheetUI As HelloSheet
Private Sub Class_Initialize()
Set sheetUI = HelloSheet
End Sub
Error 438 is thrown whenever you try to access the default member of a Worksheet class, since Worksheet has no default member - this code reproduces the error from the immediate pane:
?Sheet1
Or:
foo = Sheet1
Rubberduck inspections should have warned about this, under "Code Quality Issues":
Object variable 'sheetUI' is assigned without the 'Set' keyword.

Moving a picturebox with visual basic

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PictureBox1.Location = New Point(PictureBox1.Location.X + TextBox1.Text, PictureBox1.Location.Y) 'Timer run from left to right to the number of input
End Sub

VB6 Multiple Instances of ActiveX Object

In VB6 (due to client requirements), I need to be able to execute multiple instances of an ActiveX EXE that I wrote to download files to multiple units via RS232.
I have developed a test application that, I think mirrors what I need to do. First, an ActiveX EXE that simulates the download process called TClass. This ActiveX EXE raises events to report back its current progress as thus:
TClass.exe (ActiveX EXE, Instancing = SingleUse, Threading Model = Thread per Object)
Option Explicit
Private Declare Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long)
Public Event Progress(Value As Long)
Public SeedVal As Long
Public Sub MultByTwo()
Dim i As Integer
Dim lVal As Long
lVal = SeedVal
For i = 0 To 10
Sleep (2000)
lVal = lVal * 2
RaiseEvent Progress(lVal)
Next i
Exit Sub
End Sub
Next a wrapper class to instantiate TClass and handle the call-back events (Progress), call it WClass (AxtiveX DLL, Instancing = MultiUse, Apartment Threaded):
Option Explicit
Public WSeedVal As Long
Public WResultVal As Long
Private WithEvents MYF87 As TClass.TargetClass
Private Sub Class_Initialize()
' Set MYF87 = CreateObject("TClass.TargetClass")
Set MYF87 = New TClass.TargetClass
End Sub
Public Function Go() As Integer
MYF87.SeedVal = WSeedVal
MYF87.MultByTwo
End Function
Public Sub MYF87_Progress(Value As Long)
WResultVal = Value
DoEvents
End Sub
Public Function CloseUpShop() As Integer
Set MYF87 = Nothing
End Function
And finally the UI to instantiate WClass. This is a simple forms app:
Option Explicit
Private lc1 As WClass.WrapperClass
Private lc2 As WClass.WrapperClass
Private lc3 As WClass.WrapperClass
Private lc4 As WClass.WrapperClass
Private lc5 As WClass.WrapperClass
Private Sub cmd1_Click()
Set lc1 = CreateObject("WClass.WrapperClass")
lc1.WSeedVal = CInt(txt1.Text)
lc1.Go
End Sub
Private Sub cmd2_Click()
Set lc2 = CreateObject("WClass.WrapperClass")
lc2.WSeedVal = CInt(txt2.Text)
lc2.Go
End Sub
Private Sub cmd3_Click()
Set lc3 = CreateObject("WClass.WrapperClass")
lc3.WSeedVal = CInt(txt3.Text)
lc3.Go
End Sub
Private Sub cmd4_Click()
Set lc4 = CreateObject("WClass.WrapperClass")
lc4.WSeedVal = CInt(txt4.Text)
lc4.Go
End Sub
Private Sub cmd5_Click()
Set lc5 = CreateObject("WClass.WrapperClass")
lc5.WSeedVal = CInt(txt5.Text)
lc5.Go
End Sub
Private Sub Form_Load()
Timer1.Interval = 2000
Timer1.Enabled = True
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Not lc1 Is Nothing Then
lc1.CloseUpShop
Set lc1 = Nothing
End If
If Not lc2 Is Nothing Then
lc2.CloseUpShop
Set lc2 = Nothing
End If
If Not lc3 Is Nothing Then
lc3.CloseUpShop
Set lc3 = Nothing
End If
If Not lc4 Is Nothing Then
lc4.CloseUpShop
Set lc4 = Nothing
End If
If Not lc5 Is Nothing Then
lc5.CloseUpShop
Set lc5 = Nothing
End If
End Sub
Private Sub Timer1_Timer()
If Timer1.Enabled Then
Timer1.Enabled = False
If Not lc1 Is Nothing Then
txtRes1.Text = CStr(lc1.WResultVal)
txtRes1.Refresh
End If
If Not lc2 Is Nothing Then
txtRes2.Text = CStr(lc2.WResultVal)
txtRes2.Refresh
End If
If Not lc3 Is Nothing Then
txtRes3.Text = CStr(lc3.WResultVal)
txtRes3.Refresh
End If
If Not lc4 Is Nothing Then
txtRes4.Text = CStr(lc4.WResultVal)
txtRes4.Refresh
End If
If Not lc5 Is Nothing Then
txtRes5.Text = CStr(lc5.WResultVal)
txtRes5.Refresh
End If
Timer1.Interval = 2000
Timer1.Enabled = True
End If
DoEvents
End Sub
txt1, txt2, txt3, txt4 and txt5 are Text items that provide a seed value that ends up getting passed to TClass as a property. txtRes1, txtRes2, txtRes3, txtRes4 and txtRes5 are Text items to hold the results of TClass.MultByTwo, as reported via the RaiseEvent Progress() call. cmd1, cmd2, cmd3, cmd4 and cmd5 are tied to the corresponding _Click functions above, and instantiate WClass.WrapperClass and get everything going. The form also has a Timer object called Timer1 set to fire every 2 seconds. The only purpose of this is to update the UI from the public properties in WClass.
I have built TClass to TClass.exe and WClass to WClass.dll and referenced WClass.dll from the UI app. When I run the form and click cmd1, the first thing i notice is that the Timer1_Timer no longer fires, so my UI never gets updated. Second, if I click on cmd2, it will fire, but appears to block the execution of the first instance.
I have spent a couple days reading posts and instructions on MSDN... no luck... any help would be greatly appreciated!
Thanks!
Update: I have changed the WClass.dll wrapper class to implement the recommendation of using callback functions. See below:
V2: WClass.dll (ActiveX DLL, Apartment Threading, Instancing = MultiUse)
Option Explicit
Public WSeedVal As Long
Public WResultVal As Long
Public Event WProgress(WResultVal As Long)
Private WithEvents MyTimer As TimerLib.TimerEx
Private WithEvents MYF87 As TClass.TargetClass
Private gInterval As IntervalData
Private Sub Class_Initialize()
Set MyTimer = CreateObject("TimerLib.TimerEx")
' Set MyTimer = New TimerLib.TimerEx
Set MYF87 = CreateObject("TClass.TargetClass")
' Set MYF87 = New TClass.TargetClass
End Sub
Public Function Go() As Integer
gInterval.Second = 1
MyTimer.IntervalInfo = gInterval
MyTimer.Enabled = True
End Function
Private Sub MyTimer_OnTimer()
MyTimer.Enabled = False
MYF87.SeedVal = WSeedVal
MYF87.MultByTwo
End Sub
Public Sub MYF87_Progress(Value As Long)
WResultVal = Value
RaiseEvent WProgress(WResultVal)
DoEvents
End Sub
Public Function CloseUpShop() As Integer
Set MYF87 = Nothing
End Function
Requisite changes in UI Class:
Option Explicit
Private WithEvents lc1 As WClass.WrapperClass
Private WithEvents lc2 As WClass.WrapperClass
Private WithEvents lc3 As WClass.WrapperClass
Private WithEvents lc4 As WClass.WrapperClass
Private WithEvents lc5 As WClass.WrapperClass
Private Sub cmd1_Click()
' MsgBox ("Begin UI1.cmd1_Click")
Set lc1 = CreateObject("WClass.WrapperClass")
lc1.WSeedVal = CInt(txt1.Text)
lc1.Go
' MsgBox ("End UI1.cmd1_Click")
End Sub
Public Sub lc1_WProgress(WResultVal As Long)
txtRes1.Text = CStr(WResultVal)
txtRes1.Refresh
DoEvents
End Sub
Private Sub cmd2_Click()
Set lc2 = CreateObject("WClass.WrapperClass")
lc2.WSeedVal = CInt(txt2.Text)
lc2.Go
End Sub
Public Sub lc2_WProgress(WResultVal As Long)
txtRes2.Text = CStr(WResultVal)
txtRes2.Refresh
DoEvents
End Sub
Private Sub cmd3_Click()
Set lc3 = CreateObject("WClass.WrapperClass")
lc3.WSeedVal = CInt(txt3.Text)
lc3.Go
End Sub
Public Sub lc3_WProgress(WResultVal As Long)
txtRes3.Text = CStr(WResultVal)
txtRes3.Refresh
DoEvents
End Sub
Private Sub cmd4_Click()
Set lc4 = CreateObject("WClass.WrapperClass")
lc4.WSeedVal = CInt(txt4.Text)
lc4.Go
End Sub
Public Sub lc4_WProgress(WResultVal As Long)
txtRes4.Text = CStr(WResultVal)
txtRes4.Refresh
DoEvents
End Sub
Private Sub cmd5_Click()
Set lc5 = CreateObject("WClass.WrapperClass")
lc5.WSeedVal = CInt(txt5.Text)
lc5.Go
End Sub
Public Sub lc5_WProgress(WResultVal As Long)
txtRes5.Text = CStr(WResultVal)
txtRes5.Refresh
DoEvents
End Sub
Private Sub Form_Load()
' Timer1.Interval = 2000
' Timer1.Enabled = True
Timer1.Enabled = False
End Sub
Private Sub Form_Unload(Cancel As Integer)
If Not lc1 Is Nothing Then
lc1.CloseUpShop
Set lc1 = Nothing
End If
If Not lc2 Is Nothing Then
lc2.CloseUpShop
Set lc2 = Nothing
End If
If Not lc3 Is Nothing Then
lc3.CloseUpShop
Set lc3 = Nothing
End If
If Not lc4 Is Nothing Then
lc4.CloseUpShop
Set lc4 = Nothing
End If
If Not lc5 Is Nothing Then
lc5.CloseUpShop
Set lc5 = Nothing
End If
End Sub
I still see the same behavior... Click cmd1, then I see the results start in txtRes1. Click cmd2, results stop updating in txtRes1, and txtRes2 updates until it finishes, then txtRes1 updates.
I would not expect this to work in the VB6 debugger, as it is single-threaded, but creating an executable and running that executable still produces these same results.
I have also tried changing the way my TClass is instantiated (New versus CreateObject) - no difference noticed. I have also tried using New and CreateObject() when instantiating WClass too... still not doing what I would like it to do...
Since you did such a nice job of asking your question, making it pretty easy to set everything up, I spent a little time fooling around with this. First, your DLL and EXE work fine. Your problem is that your Timer solution to handling screen updates has sent you down the rabbit hole.
First, the Timer event never fires unless the timer is enabled, so it's useless to check the Enabled property inside the event handler. Next, when you call DoEvents, it only flushes the event queue for the current object. So, calling DoEvents in MYF87_Progress does not run your Timer event. So it isn't correct that the Timer event doesn't fire; what's happening is that all your Timer events stack up in the form's event queue and they all get executed at once when the DLL is done executing. This design, as you are finding, isn't working, and even if you figure out a way to fix it you'll have something resembling Jed Clampett's truck.
A better design is to add a Progress event to your DLL as well, raise it from your MYF87_Progress handler, and let your form handle it. (I'm assuming that the reason for your wrapper DLL is that you have more stuff to put in it that should only go in one place, otherwise I'd suggest that you simplify your design by having your form call the EXE directly.) Call DoEvents in your form handler to update the screen.
Next, this implementation cries out for control arrays. You can put each of your command buttons, each of your sets of five text boxes, and each of your DLL instances in an array. This will greatly simplify the work you have to do. In fact, your entire Form code is pretty much reducible to this (plus the event handler I've mentioned):
Option Explicit
Private lc(4) As WClass.WrapperClass
Private Sub cmd_Click(Index As Integer)
Set lc(Index) = CreateObject("WClass.WrapperClass")
With lc(Index)
.WSeedVal = CInt(txt(Index).Text)
.Go
txtRes(Index).Text = CStr(.WResultVal)
End With
End Sub
This code will show the end result each time you push a button, but won't keep updating your text boxes every time there's a change posted from your EXE. To do that, you'll need to put in that event logic. I'll leave that to you since you appear to know how to do it already.
Suppose you have a go at all that, and post back if you have problems.
p. s. to make a control array, simply make all the controls in the array have the same name, and set the Index property to 0, 1, 2, 3, etc.
p. p. s. I forgot that you can't put WithEvents with an object array. I'm going to mess around with this and see if there's a way to get the objects in an array, but it might be necessary to have separate variables as you have them now.

How to tell which form module called your class

Suppose, for instance, that I want a method that adds a ComboBox. Maybe I try this
Public Sub AddComboBox()
Dim cb As MSForms.ComboBox
Set cb = <Calling form module>.Controls.Add("Forms.ComboBox.1")
End Sub
How can I get <Calling form module>?
As others have said, pass the instance of the form to class method. Unlike others, I'm going to add:
Declare the argument AS
MSForms.UserForm
Pass the parameter ByVal.
If calling from the UserForm itself,
use the Me keyword in the call.
He's a brief example:
' <Module1.bas>
Option Explicit
Sub Main()
UserForm1.Show vbModeless
UserForm2.Show vbModeless
End Sub
' </Module1.bas>
' <UserForm1.frm>
Option Explicit
Private Sub UserForm_Activate()
Dim c As Class1
Set c = New Class1
c.AddComboBox Me
End Sub
' </UserForm1.frm>
' <UserForm2.frm>
Option Explicit
Private Sub UserForm_Activate()
Dim c As Class1
Set c = New Class1
c.AddComboBox Me
End Sub
' </UserForm2.frm>
' <Class1.cls>
Option Explicit
Public Sub AddComboBox(ByVal MSForms_UserForm As MSForms.UserForm)
Dim cb As MSForms.ComboBox
Set cb = MSForms_UserForm.Controls.Add("Forms.ComboBox.1")
End Sub
' </Class1.cls>
I think you're writing this the wrong way. Instead of trying to determine who called the method, just pass the <Calling Form Module> to AddComboBox() as an argument. Like this:
Public Sub CallToAddComboBox()
AddComboBox(<Calling form module>)
End Sub
Public Sub AddComboBox(CallingFormModule as <Module Object Type>)
Dim cb As MSForms.ComboBox
Set cb = CallingFormModule.Controls.Add("Forms.ComboBox.1")
End Sub

Resources