Setting (Private WithEvents As Sheet1) sheetUI = Sheet1 导致错误 438: Object doesn't support this 属性 or method

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

我正在使用 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/ 博客。

我创建了一个 "bare bones" 项目,没有任何模型包含 Excel 作品sheet (HelloSheet)、一个视图、一个 ViewAdapter(包括 ViewCommands 和 ViewEvents 接口)和一个控制器. VBA 项目编译没有错误,但是当我尝试 运行 "application entry" 宏时,我得到了可怕的 "Run-time error 438: Object doesn't support this property or method"。这发生在我的视图 class 的 Class_Initialize() 子内部,我在其中声明了 "Private WithEvents sheetUI As HelloSheet" 并尝试设置 "sheetUI = HelloSheet"。

Here 是我的项目树的概览,如 RubberDuck VBIDE 中所示。

我已尝试更新 VBA 项目引用以与 "Battleship" 示例项目的引用完全匹配。我还尝试了两种不同的方法来实现 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,更具体地说:

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

..但 "Run-time error 438: Object doesn't support this property or method" 仍然存在。

以下是 sheets、classes、接口等中的所有相关代码拆分:

1) HelloSheet(常规 Excel sheet 代码隐藏):

'@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) 工作表视图 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 接口:

'@Folder("HelloWorld.View")
'@Interface
Option Explicit

Public Sub PreviewSelectedRecord(ByVal selectedRow As Integer)
End Sub

4) ISheetViewCommands 接口:

'@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 / 有默认实例):

'@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) 最后是作为入口点的 "Macros" 标准模块。这是我遇到错误的地方("Set view = New SheetView" 行):

'@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

假设我可以绕过入门级错误,我希望有一个非常简单的功能:

1) 隐藏的 Excel 形状在 HelloSheet(OnLaunchCommand)上可见;

2) 当双击一个单元格时,它所在的行将在同一作品的单元格A1中报告sheet(Worksheet_BeforeDoubleClick事件)

显然,如此简单的任务需要大量代码 - 我的想法是,一旦我掌握了这些基础知识,就可以将模型 classes 添加到项目并将它们映射到某些区域(即 Tables/ListObjects) 在工作簿中。

任何帮助将不胜感激!并感谢所有完成这篇相当长的文章的人 post :)

Private WithEvents sheetUI As HelloSheet

Private Sub Class_Initialize()
    sheetUI = HelloSheet
End Sub

sheetUI是对象引用,赋值需要Set关键字:

Private WithEvents sheetUI As HelloSheet

Private Sub Class_Initialize()
    Set sheetUI = HelloSheet
End Sub

每当您尝试访问 Worksheet class 的默认成员时都会抛出错误 438,因为 Worksheet 没有默认成员 - 此代码重现了 直接窗格:

?Sheet1

或:

foo = Sheet1

Rubberduck 检查 应该 对此发出警告,在 "Code Quality Issues":

Object variable 'sheetUI' is assigned without the 'Set' keyword.