在用户窗体中使用类模块变量(需要错误“ 424”对象)

时间:2019-02-07 10:38:41

标签: excel vba class

我有一个类模块,其中包含一个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
'....

我很肯定,我无法理解这些属性的工作原理,这使我感到沮丧。如果有人可以对我做错的事情有所了解,请

2 个答案:

答案 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}},然后显示用户表单也可以访问oWBoWB.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中的代码将cellrangecellr解释为在用户表单本身中声明的局部变量。您没有在用户表单中声明这样的变量,并且您没有使用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.cellm_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