【发布时间】:2023-04-06 05:40:01
【问题描述】:
我正在尝试执行this post in codereview的建议
目标:
管理用户与 Excel 表格 (ListObjects) 交互时发生的情况
最后的想法是为不同的表格设置自定义事件。例如当您向 table1 添加一行时,会引发自定义 AddEvent1,当您对 table2 执行相同操作时,会引发 AddEvent2。
只有一个类来管理事件,一个类来保存表格及其信息。
所以建议的过程是:
- 将列表对象添加到名为
Table的类中 - 该类将监听父工作表上的事件(
Change和SelectionChange) - 触发更改事件时,从处理这些事件的类
TableManager触发自定义事件(如adding、updating或deleting行之类的事件)
编辑#1:
调整代码:
-
Create函数现在返回Table的实例 - 属性
Set SourceTable现在将listObjectParentSheet字段设置为相应的值
但Table Manager 仍然没有监听listObjectParentSheet_Change 中引发的事件
组件:
1) 带有 Excel 表格 (ListObject) 的工作表和后面的代码:
Private Sub Worksheet_Activate()
Dim myTable As Table
Dim myTableManager As TableManager
Set myTable = Table.Create(Me.ListObjects(1))
Set myTableManager = New TableManager
Set myTableManager.TableInstance = myTable
End Sub
2) 类 Table(使用 rubberduck 将预先声明的 id 设置为 true)
'@Folder("VBAProject")
Option Explicit
'@PredeclaredId
Private Type TTable
SourceTable As ListObject
End Type
Private this As TTable
Private WithEvents listObjectParentSheet As Excel.Worksheet
Public Event AddEvent()
Public Property Get SourceTable() As ListObject
Set SourceTable = this.SourceTable
End Property
Public Property Set SourceTable(ByVal value As ListObject)
Set this.SourceTable = value
Set listObjectParentSheet = value.Parent
End Property
Public Property Get Self() As Table
Set Self = Me
End Property
Public Function Create(ByVal EvalSourceTable As ListObject) As Table
With New Table
Set .SourceTable = EvalSourceTable
Set Create = .Self
End With
End Function
Private Sub listObjectParentSheet_Change(ByVal Target As Range)
If Not Intersect(Target, SourceTable.DataBodyRange) Is Nothing Then
MsgBox listObjectParentSheet.Name & " " & Target.Address
RaiseEvent AddEvent
End If
End Sub
3) 类TableManager
Option Explicit
Private WithEvents m_table As Table
Public Property Get TableInstance() As Table
Set TableInstance = m_table
End Property
Public Property Set TableInstance(ByRef tableObject As Table)
Set m_table = tableObject
End Property
Private Sub m_table_AddEvent()
MsgBox "Adding something"
End Sub
问题/问题:
我还没有弄清楚如何在TableManager 类中触发“AddEvent”。我知道我搞砸了一些实例化类的概念,但我不知道我做错了什么。
预期结果:
当用户更改列表对象的任何单元格时,在引发AddEvent 时显示消息框“添加内容”
任何帮助将不胜感激。
编辑#2
感谢 Mat 回答的最终代码:
表:Sheet1:
Private Sub Worksheet_Activate()
With TableManager
Set .TableEvents = Table.Create(Sheet1.ListObjects(1))
End With
End Sub
模块:ListObjectUtilities
Option Explicit
Public Function GetCellRow(ByVal EvalTable As ListObject, ByVal EvalCell As Range) As Long
If Intersect(EvalCell, EvalTable.DataBodyRange) Is Nothing Then Exit Function
GetCellRow = EvalCell.Row - EvalTable.HeaderRowRange.Row
End Function
Public Function GetCellColumn(ByVal EvalTable As ListObject, ByVal EvalCell As Range) As Long
If Intersect(EvalCell, EvalTable.DataBodyRange) Is Nothing Then Exit Function
GetCellColumn = EvalCell.Column - EvalTable.HeaderRowRange.Column + 1
End Function
班级:ITable
Option Explicit
Public Property Get SourceTable() As ListObject
End Property
班级:Table
'@Folder("VBAProject")
'@PredeclaredId
Option Explicit
Private WithEvents TableSheet As Excel.Worksheet
Private Type TTable
SourceTable As ListObject
LastRowCount As Long
LastColumnCount As Long
End Type
Private this As TTable
Public Event Changed(ByVal cell As Range)
Public Event AddedNewRow(ByVal newRow As ListRow)
Public Event AddedNewColumn(ByVal newColumn As ListColumn)
Implements ITable
Public Function Create(ByVal Source As ListObject) As ITable
With New Table
Set .SourceTable = Source
Set Create = .Self
End With
End Function
Public Property Get Self() As Table
Set Self = Me
End Property
Public Property Get SourceTable() As ListObject
Set SourceTable = this.SourceTable
End Property
Public Property Set SourceTable(ByVal value As ListObject)
ThrowIfSet this.SourceTable
ThrowIfNothing value
Set TableSheet = value.Parent
Set this.SourceTable = value
Resize
End Property
Friend Sub OnChanged(ByVal Target As Range)
RaiseEvent Changed(Target)
End Sub
Friend Sub OnAddedNewRow(ByVal newRow As ListRow)
RaiseEvent AddedNewRow(newRow)
End Sub
Friend Sub OnAddedNewColumn(ByVal newColumn As ListColumn)
RaiseEvent AddedNewColumn(newColumn)
End Sub
Private Sub ThrowIfNothing(ByVal Target As Object)
If Target Is Nothing Then Err.Raise 5, TypeName(Me), "Argument cannot be a null reference."
End Sub
Private Sub ThrowIfSet(ByVal Target As Object)
If Not Target Is Nothing Then Err.Raise 5, TypeName(Me), "This reference is already set."
End Sub
Private Sub Resize()
With this.SourceTable
this.LastRowCount = .ListRows.Count
this.LastColumnCount = .ListColumns.Count
End With
End Sub
Private Sub TableSheet_Change(ByVal Target As Range)
If Intersect(Target, SourceTable.DataBodyRange) Is Nothing Then Exit Sub
Select Case True
Case this.SourceTable.DataBodyRange.Columns.Count > this.LastColumnCount
OnAddedNewColumn SourceTable.ListColumns(ListObjectUtilities.GetCellColumn(this.SourceTable, Target))
Case this.SourceTable.DataBodyRange.Rows.Count > this.LastRowCount
OnAddedNewRow SourceTable.ListRows(ListObjectUtilities.GetCellRow(this.SourceTable, Target))
Case Else
OnChanged Target
End Select
Resize
End Sub
Private Property Get ITable_SourceTable() As ListObject
Set ITable_SourceTable = this.SourceTable
End Property
班级:TableManager
'@Folder("VBAProject")
'@PredeclaredId
Option Explicit
Private WithEvents MyTable As Table
Public Property Get TableEvents() As Table
Set TableEvents = MyTable
End Property
Public Property Set TableEvents(ByVal value As Table)
Set MyTable = value
End Property
Private Sub MyTable_AddedNewColumn(ByVal newColumn As ListColumn)
MsgBox "Added new column " & newColumn.Range.Column
End Sub
Private Sub MyTable_AddedNewRow(ByVal newRow As ListRow)
MsgBox "Added new row " & newRow.Range.Row
End Sub
Private Sub MyTable_Changed(ByVal cell As Range)
MsgBox "Changed " & cell.Address
End Sub
【问题讨论】:
-
您只能从定义它的类中触发事件。您是否试图从
TableManager内部解雇Table.AddEvent?我不明白你为什么要这样做...... -
部分问题是
Create工厂方法没有创建任何东西......它正在改变默认实例,使其成为有状态...... -
@MathieuGuindon 感谢您的回复。是的,我正在尝试触发 Table.AddEvent。我尝试使用
With New创建一个实例,但是我无法将listObjectParentSheet变量设置为父工作表。 -
那就是
Set .SourceTable = EvalSourceTable...您不能分配给私有字段。而那个 setter 应该分配WithEvents字段。 -
@MathieuGuindon 我以前有过这种方式,但我如何设置 listObjectParentSheet 变量只监听持有 ListObject 的工作表?您同意here 建议的方法吗?我认为它简化了一些东西,但我还没有弄清楚如何实现它。
标签: excel vba events rubberduck