【问题标题】:VBA class module: get property from an object using another propertyVBA 类模块:使用另一个属性从对象获取属性
【发布时间】:2017-10-25 14:34:11
【问题描述】:

所有, 我正在 VBA 中设置一个类模块结构来添加具有多个里程碑的计划,但我对它很陌生。我做了以下事情:

  • 一个名为“Plan”的类模块,它包含一个“名称”属性(字符串)和一个“里程碑”属性(里程碑类)。
  • 此里程碑类模块是名为“里程碑”的类模块的对象集合。
  • “里程碑”类具有“名称”属性和“值”属性。

所以在我的模块中,我现在正在为特定计划指定里程碑:

Plan.Milestones.Add "MilestoneA", Cells(i, 5)
Plan.Milestones.Add "MilestoneB", Cells(i, 7)
...

到目前为止一切都很好。现在对于 MilestoneC,我想知道 MilestoneA 的价值。如何获取名为“MilestoneA”的里程碑的值。

我知道下面的代码会给我答案,但我不想硬编码“item(1)”(我想使用名称):

Plan.Milestones.Item(1).Value

在 clsMilestones 类中:

Private prvt_Milestones As New Collection

Property Get Item(Index As Variant) As clsMilestone
    Set Item = prvt_Milestones(Index)
End Property

Sub Add(param_Name As String, param_Value As String)

    Dim new_milestone As clsMilestone
    Set new_milestone = New clsMilestone

    new_milestone.Name = param_Name
    new_milestone.Value = param_Value

    prvt_Milestones.Add new_milestone
End Sub

【问题讨论】:

    标签: excel vba class object


    【解决方案1】:

    您的Milestones 类是一个集合类。按照惯例,集合类有一个Item 属性,它是类的默认成员。您无法在 VBA 中轻松指定类的默认成员,但这并非不可能。

    导出代码文件,在记事本中打开。找到您的 Public Property Get Item 成员并添加 VB_UserMemId 属性 - 当您在那里时,您也可以添加 VB_Description 属性:

    Public Property Get Item(ByVal Index As Variant) As Milestone
    Attribute Item.VB_UserMemId = 0
    Attribute Item.VB_Description = "Gets the item at the specified index, or with the specified name."
        Set Item = prvt_Milestones(Index)
    End Property
    

    UserMemId = 0 使该属性成为类的默认成员 - 请注意,类中只有一个成员可以具有该值。

    暂时不要保存和关闭。

    您还需要使您的集合类也与For Each 循环一起工作,为此您需要一个返回IUnknown 的NewEnum 属性,并带有许多属性和标志:

    Public Property Get NewEnum() As IUnknown
    Attribute NewEnum.VB_Description = "Gets an enumerator that iterates through the collection."
    Attribute NewEnum.VB_UserMemId = -4
    Attribute NewEnum.VB_MemberFlags = "40"    
        Set NewEnum = prvt_Milestones.[_NewEnum]
    End Property
    

    请注意,您的内部封装 Collection 有一个隐藏成员,其名称以下划线开头 - 这在 VBA 中是非法的,因此要调用它,您需要用方括号将其括起来。

    现在这段代码是合法的:

    Dim ms As Milestone
    For Each ms In Plan.Milestones
        Debug.Print ms.Name, ms.Value ', ms.DateDue, ...
    Next
    

    保存文件,关闭它,然后将其重新导入到您的项目中。

    由于您使用字符串键填充集合(至少您的 Add 方法似乎正在这样做),因此客户端代码可以使用索引或键来检索项目。

    现在Item 是类的默认成员,这现在是合法的:

    Set milestoneA = Plan.Milestones("Milestone A").Value
    

    请注意,您的 Add 方法在添加到内部集合时需要为 Key 参数指定一个值 - 如果您想要以 Name 为键的项目,请使用 Name 作为键:

    Public Sub Add(ByVal Name As String, ByVal Value As Variant)
        Dim new_milestone As Milestone
        Set new_milestone = New Milestone
    
        new_milestone.Name = Name
        new_milestone.Value = Value
    
        prvt_Milestones.Add new_milestone, Name
    End Sub
    

    【讨论】:

    • 请注意,我删除了类名上的 cls 前缀。我从来没有在任何对象模型中看到任何带有cls 前缀的类,例如,它散发着Systems Hungarian 符号which was nothing more than an unfortunate misunderstanding 的味道,而且不应该出现。
    • +1 做得很好。我肯定会对您添加的 NewEnum 属性的更多细节感兴趣(我相信很多人都会)。
    • 好的,我刚刚测试了它,它很漂亮。谢谢马特的马克杯
    【解决方案2】:

    在plan class 中使用Milestone 类中的dictionary,并将key 设置为“Milestone_x”,将item 设置为milestone class

    那你可以说Plan.Milestones("Milestone99")

    【讨论】:

      【解决方案3】:

      向 Milestones 类添加一个属性,该属性根据名称返回里程碑:

      Property Get SelectByName(strMilestoneName as string) as clsMilestone
           Dim vIndex
           'Add code here to find the index of the milestone in question
           vIndex = ????????
      
           Set SelectByName = prvt_Milestones(Index)
      End Property
      

      或

      编辑项目属性以允许按索引或名称进行选择:

      Property Get Item(Index As Variant) As clsMilestone
          If isNumeric(Index) then
             Set Item = prvt_Milestones(Index)
          Else
             'Find Item based on Name
              Dim vIndex
              vIndex = ?????
             Set Item = prvt_Milestones(vIndex)
           End If
      End Property
      

      【讨论】:

      • 我想这意味着我仍然需要遍历所有里程碑来找到相应的项目,对吧?
      • @Hans 如果您在将项目添加到集合时指定键,则无需迭代任何内容或使用字典。但是你没有向我们展示你的Add方法,所以我们都必须做出假设。
      猜你喜欢
      • 2013-07-03
      • 1970-01-01
      • 2021-05-13
      • 1970-01-01
      • 2020-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-14
      相关资源
      最近更新 更多