【问题标题】:VBA: User defined type as event parameter in public object moduleVBA:用户定义类型作为公共对象模块中的事件参数
【发布时间】:2021-02-23 22:18:47
【问题描述】:

在我的 excel 工作簿中,我想实现一个事件发送者 接收者概念 (OOP)。

这通常有效,但我不能将用户定义类型 (UDT) 作为事件参数从一个公共工作表类传递给另一个。

UDT (tCompliance) 在标准模块中被定义为公共:

Option Explicit

Public Type tCompliance
  deliverable As String
  businessObject As String
  process As String
  instruction As String
  classification As String
End Type

发件人表“tblCompliance”的代码如下:

Option Explicit

Public Event tableChanged(compliance As tCompliance)

Private Sub Worksheet_Change(ByVal Target As Range)
  Dim lr As ListRow, compliance As tCompliance
  
  If Not Intersect(Target, table.DataBodyRange) Is Nothing Then
    Set lr = getListRow(table, Target)
    With compliance
      .deliverable = lr.Range(1, 1).value
      .process = lr.Range(1, 2).value
      .instruction = lr.Range(1, 3).value
      .classification = lr.Range(1, 4).value
    End With
    RaiseEvent tableChanged(compliance)
  End If
End Sub

接收表“tblPlanning”的代码如下:

Option Explicit

Private WithEvents me_tblCompliance As tblCompliance

Private Sub me_tblCompliance_tableChanged(compliance As tCompliance)
  // Stuff here
end Sub

我收到的错误消息组合起来没有多大意义。

第一个错误提示 “只有在 PUBLIC OBJECT MODULES ... 中定义的用户定义类型才能传递给运行时函数。”

显然,解决方案是将 UDT 定义放入公共对象模块中。 我把它放到了 PUBLIC OBJECT MODULE "thisWorkbook" 中,并再次尝试编译代码。

但随后我收到第二条错误消息:无法在 OBJECT MODULE 中定义公共定义类型

所以我尝试将 UDT 放入我在 VBE 属性部分中创建“2 - PublicNotCreatable”的类模块中。

但随后我收到相同的错误消息:无法在 OBJECT MODULE 中定义公共用户定义类型”

VBA 在这里似乎很不一致。

【问题讨论】:

  • UDT 有点像 VBA 中的红鲱鱼。可以在模块/类内使用,但在其他方面有问题。快速而肮脏的替换是一个变体(包含一个数组)和一个与您的 UDT 字段匹配的公共枚举,或者您将 UDT 转换为类的适当工作。
  • 我也有这个想法,并切换到这个解决方案。它似乎工作。至少提到的错误消息消失了。类模块必须是“2 - PublicNotCreatable”才能工作。
  • 它也可以在工作表模块中工作,但需要进行一些小调整...

标签: vba user-defined-types


【解决方案1】:

为了在工作表模块中工作,上面的代码应该如下修改

  1. 收货单代码应按以下方式调整:
Option Explicit

Private WithEvents me_tblCompliance As tblCompliance 'tblCompliance  should be the sheet code module class

Public Sub me_tblCompliance_tableChanged(compliance As tCompliance) 'Mandatory to be Public
  ''' Stuff here
   MsgBox compliance.deliverable & " - " & compliance.instruction
End Sub
  1. 引发事件的行应该从
RaiseEvent tableChanged(compliance)

RaiseEvent tblPlanning.me_tblCompliance_tableChanged(compliance)

代码必须不像在标准模块中那样调用事件,而是使用工作表代码模块类(tblPlanning)完全限定:

然后,应该调用事件本身 (me_tblCompliance_tableChanged)

请以这种方式进行测试并发送一些反馈。

无论如何,有趣的问题... :)

【讨论】:

  • @FaneDuru:我会测试它并给你反馈。我能说的是,名称“tblCompliance”和“tblPlanning”已经是工作表“类”的代号。
  • @user14181536:如果是这样,最好将其用作tblCompliance。当在双引号之间使用时,它(此处)被理解为工作表名称......我将以这种方式调整代码,提到 tblCompliance 是什么,或者应该是什么。但我测试了这个解决方案,它没有任何问题。当然,我的测试工作簿上有两张现有的工作表......
  • @FaneDuru:我根据您的建议测试了更改后的代码,并且可以正常工作。谢谢你。 Key 是RaiseEvent tblPlanning.me_tblCompliance_tableChanged(compliance) 行中的完全限定名称。我唯一能提到的是,在所描述的解决方案中,发送方代码模块必须知道接收代码模块的处理函数名称。这符合 OOP 原则吗?如果我有几个接收代码模块怎么办?也许我的想法在这里是错误的......
  • @user14181536:恐怕这是唯一的可能性......即使是一个简单的过程也不能在没有完全限定的情况下从工作表模块中调用,就像在标准模块中一样。看起来所有能够引发事件的对象都以这种方式运行。从理论上讲,只有一个类可以包装事件,然后将其分配给您需要的对象。在动态创建控件的情况下,我知道如何为标准/定义事件执行此操作,但我什至从未尝试过自定义事件。忙了一阵子,以后可能会试一试……
  • @FaneDuru:感谢您的帮助。在我看来,提供的解决方案是对该问题的有效答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-22
  • 2011-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多