我有一个类模块,其中包含一个worksheet_change子项,并且必须在该子项中弹出一个用户窗体。我想在Userform的代码中使用来自类模块的许多变量。但是,无论我做什么,都无法正常工作。
我尝试使用this very lenghty guide中的方法,但无济于事。 SO上的其他线程无法帮助我。
Private cell As Range
Public WithEvents m_wb As Workbook
Property Get cellr() As Range
Set cellr = cell
End Property
Property Set cellr(cellrange As Range)
Set cell = cellrange
End Property
Public Property Set Workbook(wb As Workbook)
Set m_wb = wb
End Property
Public Property Get Workbook() As Workbook
Set Workbook = m_wb
End Property
Public Sub m_wb_SheetChange(ByVal Sh As Object, ByVal Target As Range) 'simplified, but accurate
Application.EnableEvents = False
For each cell in Target
ReplaceTask.Show
Next cell
Application.EnableEvents = True
End Sub
在userform_initialize
宏中,我需要能够获取m_wb
工作簿的名称以及单元格(最好是range
变量,否则只需地址即可) )在For each cell in Target
循环中。对于下面代码中的每个变量,我得到
需要错误“ 424”对象
显示变量不是公共的。
Private Sub UserForm_Initialize()
Debug.Print cellrange.Address
Debug.Print cell.Address
Debug.Print cellr.Address
Debug.Print m_wb.Name
'....
我很肯定,我无法理解这些属性的工作原理,这使我感到沮丧。如果有人可以对我做错的事情有所了解,请
答案 0 :(得分:2)
要执行此操作,它至少需要一个类类型的公共对象变量。并且必须将此对象变量设置为类的新实例。然后,这个对象变量(只有这个对象变量)是您的类的公共可访问实例。
示例:
让您的班级命名为clsWorkbook
并使用以下代码:
Option Explicit
Private m_cell As Range
Private WithEvents m_wb As Workbook
Property Let cell(cellrange As Range)
Set m_cell = cellrange
End Property
Property Get cell() As Range
Set cell = m_cell
End Property
Public Property Let Workbook(wb As Workbook)
Set m_wb = wb
End Property
Public Property Get Workbook() As Workbook
Set Workbook = m_wb
End Property
Private Sub m_wb_SheetChange(ByVal Sh As Object, ByVal Target As Range) 'simplified, but accurate
Application.EnableEvents = False
For Each m_cell In Target
ReplaceTask.Show
Next m_cell
Application.EnableEvents = True
End Sub
让您名为ReplaceTask
的用户表单具有以下代码:
Option Explicit
Private Sub UserForm_Initialize()
Debug.Print oWB.Workbook.Name
Debug.Print oWB.cell.Address
End Sub
在默认模块中,具有以下代码:
Option Explicit
Public oWB As clsWorkbook
Public Sub test()
Set oWB = New clsWorkbook
oWB.Workbook = ThisWorkbook
End Sub
现在,在运行Sub test()
之后,在代码所在的工作簿中的工作表中进行更改。这应该触发类对象Sub m_wb_SheetChange(ByVal Sh As Object, ByVal Target As Range)
的{{1}},然后显示用户表单也可以访问oWB
和oWB.Workbook.Name
。
由于讨论了oWB.cell.Address
全局实例的必要性,让我们举一个完整的示例,该示例可以重构并显示clsWorkbook
如何成为私有类成员:
让您的班级命名为clsWorkbook
并使用以下代码:
clsWorkbook
让您名为Option Explicit
Private m_cell As Range
Private WithEvents m_wb As Workbook
Property Let Cell(cellrange As Range)
Set m_cell = cellrange
End Property
Property Get Cell() As Range
Set Cell = m_cell
End Property
Property Let Workbook(wb As Workbook)
Set m_wb = wb
End Property
Property Get Workbook() As Workbook
Set Workbook = m_wb
End Property
Private Sub m_wb_SheetChange(ByVal Sh As Object, ByVal Target As Range) 'simplified, but accurate
Application.EnableEvents = False
Dim frm As ReplaceTask
For Each m_cell In Target
Set frm = New ReplaceTask
frm.Init Me
frm.Show
Next m_cell
Application.EnableEvents = True
End Sub
的用户表单具有以下代码:
ReplaceTask
在默认的类模块Option Explicit
Private m_ParentClass As clsWorkbook
Friend Sub Init(ByVal p As clsWorkbook)
Set m_ParentClass = p
Me.Caption = p.Workbook.Name & " : " & p.Cell.Address
End Sub
中具有以下代码:
ThisWorkbook
现在Option Explicit
Private oWB As clsWorkbook
Private Sub Workbook_Open()
Set oWB = New clsWorkbook
oWB.Workbook = Workbooks.Open("P:/Mappe1.xlsx")
End Sub
在工作簿打开时被实例化,并且是clsWorkbook
的私有成员,而它的工作簿成员是另外打开的工作簿。 ThisWorkbook
SheetChange
实例在此监听clsWorkbook
。
并且由于oWB
用户表单是在ReplaceTask
中实例化的,并且被赋予了类实例作为参数,因此该用户表单也知道该类的成员。
答案 1 :(得分:1)
用户窗体和带有事件处理程序的类是两个不同的作用域。您不能期望能够在不限定该范围的情况下引用其他范围的成员。 UserForm_Initialize
中的代码将cellrange
和cellr
解释为在用户表单本身中声明的局部变量。您没有在用户表单中声明这样的变量,并且您没有使用Option Explicit
,因此,当代码隐式假定其为Dim cellrange As Variant
时,您将得到运行时错误424,而不是编译时错误从未初始化过,因此为Empty
。
要解决此问题,您需要告诉用户实例应从中获取事件处理类的实例的属性。为此,将其放在UserForm中就足够了:
Private m_ParentClass As ThatClassThatCreatesForms
Friend Sub Init(ByVal p As ThatClassThatCreatesForms)
Set m_ParentClass = p
End Sub
,并将父类中的For Each
循环更改为:
For each cell in Target
ReplaceTask.Init Me
ReplaceTask.Show
Next cell
您必须具有单独的“ Init”方法,因为VBA类不能具有带参数的构造函数。
然后,ReplaceTask
中的代码可以使用m_ParentClass.cell
,m_ParentClass.Workbook
等。但是由于尚未调用UserForm_Initialize
,因此无法从Init
进行操作。但这不是问题,只需将代码从UserForm_Initialize
移到Init
。
要更进一步,我建议您停止使用implicit form instance。手动创建实例是一个好习惯:
For each cell in Target
Dim f As ReplaceTask
Set f = New ReplaceTask
f.Init Me
f.Show
Next cell