当工作表的代码隐藏在“代理”接口+类中实现时,正确处理工作表事件

2024-03-24

我正在努力进一步发展这个很好的例子 https://www.dropbox.com/s/vozhez4wwdlfg8f/WorkbookProxyExampleDialog.xlsm?dl=0,它已经实现了这些非常有见地的 RubberduckVBA.com 文章中讨论的最佳实践:

  1. 通过以下方式从 Excel 工作簿/工作表中抽象出来代理类 https://rubberduckvba.wordpress.com/2017/12/08/there-is-no-worksheet/;
  2. 使用用户窗体控件 https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/不会弄乱其默认实例的状态;
  3. 将“应用”逻辑添加到#2 https://rubberduckvba.wordpress.com/2018/05/08/apply-logic-for-userform-dialog/.

我想添加到现有的例子 https://www.dropbox.com/s/vozhez4wwdlfg8f/WorkbookProxyExampleDialog.xlsm?dl=0一个事件处理程序,它(为了简单起见)报告 Sheet1 的“A1”单元格中 Sheet2 的“已更改”范围的左上角单元格的值,以及“A2”中的更改时间。我通常会在 Sheet2 的代码隐藏中这样做:

Private Sub Worksheet_Change(ByVal Target As Range)
    Sheet1.Cells(1, 1).Value2 = Target.Cells(1, 1).Value2
    Sheet1.Cells(1, 2).Value2 = CStr(Now)
End Sub

但我想知道如何最好地实现这一目标给出的例子 https://www.dropbox.com/s/vozhez4wwdlfg8f/WorkbookProxyExampleDialog.xlsm?dl=0,考虑到它是围绕 MVP 模式设计的,并通过代理接口利用工作簿和工作表抽象 - 这意味着零/最小工作表代码隐藏是预期的。

我能够理解事件处理是如何在 Awesome 中实现的战舰教程 https://rubberduckvba.wordpress.com/2018/09/04/oop-battleship-part-3-the-view/但其设计在一些重要方面有所不同:

  1. 《战舰》遵循 MVC 设计模式,而我想坚持使用 MVP,如示例所示;
  2. “战舰”通过“视图”类从其工作表中抽象出来,而我希望每个工作表都有一个单独的代理接口+类;
  3. “战舰”部署适配器模式 https://stackoverflow.com/questions/41023670/can-we-use-interfaces-and-events-together-at-the-same-time/45825831#45825831虽然我很乐意将视图和工作表代理实现与演示者结合起来(如果可能的话,在事件处理方面)。

考虑到这一点,我绝对希望看到一个代码示例,它将我上面描述的“Worksheet_Change”事件添加到基础工程 https://www.dropbox.com/s/vozhez4wwdlfg8f/WorkbookProxyExampleDialog.xlsm?dl=0它已经实现了 Workbook 和 Worksheet 代理并遵循 MVP 模式。

即使没有代码示例,如果我解决了这些问题也会有很大帮助:

  1. 是否工作表代理方法 https://rubberduckvba.wordpress.com/2017/12/08/there-is-no-worksheet/规定应该有绝对零的工作表代码隐藏?如果我像这样在 Sheet2 (不是它的代理)内开始“Worksheet_Change”事件实现,这会是朝着错误方向迈出的一步吗:
Public Event SheetChanged(ByVal changedRange As Range)

Private Sub Worksheet_Change(ByVal Target As Range)
    RaiseEvent SheetChanged(Target)
End Sub
  1. 如果不是绝对有必要使用适配器模式 https://stackoverflow.com/questions/41023670/can-we-use-interfaces-and-events-together-at-the-same-time/45825831#45825831对于事件处理,使用“IViewCommands”和“IViewEvents”接口分别列出从演示者发送到视图的所有命令以及从视图引发并发送到演示者的事件仍然是一个好主意吗?
  2. 我想我需要使用惰性对象/弱引用 https://rubberduckvba.wordpress.com/2018/09/11/lazy-object-weak-reference/能够揭露事件。如果是这样,并且假设我可以在没有适配器的情况下完成工作(请参见上面的#2),这是否意味着我的“Sheet2Proxy”类必须通过其“IViewEvents”(再次参见# 2)接口?

你正在抽象Worksheet在“代理”类后面;根据定义,它与工作表耦合,您想要的是确保抽象是密封的,以免您看到有漏洞的抽象并最终耦合其他代码Excel.Worksheet类型,这违背了整个目的。

对于项目的其余部分,工作表代理类充当facade操纵并理解有关特定对象的所有知识Excel.Worksheet:这样做的结果是您现在可以使用两个模块来抽象工作表事物 - 工作表本身和代理类:

  • 工作表代码隐藏可以抽象诸如ListObject/tables、命名范围等;使用Property Get代理可以使用的成员。
  • 工作表代理类从其余代码中抽象出工作表操作。

事实上,这种方法并没有为实际工作表代码隐藏留下太多空间/需要:我将开始对代理类中的所有内容进行编码,如果该模块变得太冗长,或者如果我发现它的抽象级别需要获得一个高一点,然后我会将较低级别的内容移至工作表本身的代码隐藏中。

Workheet模块和其他文档模块不应实现接口 - 让工作表实现接口是混淆 VBA 并使 VBA 崩溃的好方法:不要这样做。所以这可能是你的隐藏代码:

Option Explicit

Public Property Get SomeSpecificRange() As Range
    Set SomeSpecificRange = Me.Names("SomeSpecificRange").RefersToRange
End Property

那么代理类可以这样做:

Option Explicit
Private sheetUI As Sheet1
Private WithEvents sheet As Worksheet

Private Sub Class_Initialize()
    Set sheet = Sheet1
    Set sheetUI = Sheet1
End Sub

Private Sub sheet_Change(ByVal Target As Range)
    If Intersect(Target, sheetUI.SomeSpecificRange) Then
        '...
    End If
End Sub

因此,代理类可以在没有整个适配器管道的情况下很好地处理工作表事件。它还可以通过其公开的处理来自演示者的命令Public成员。

但是代理类又名“抽象工作表”并不是响应事件的正确位置:它是需要运行节目的演示者。

因此,您可以让代理触发一个事件来响应工作表事件,将消息打包并将消息转发给演示者:

Option Explicit
Public Event SomeSpecificRangeChanged()
Private sheetUI As Sheet1
Private WithEvents sheet As Worksheet

Private Sub Class_Initialize()
    Set sheet = Sheet1
    Set sheetUI = Sheet1
End Sub

Private Sub sheet_Change(ByVal Target As Range)
    If Intersect(Target, sheetUI.SomeSpecificRange) Then
        RaiseEvent SomeSpecificRangeChanged
    End If
End Sub

然后演示者可以处理SomeSpecificRangeChanged关闭代理类 - 启动一些用户表单,启动一些数据库查询,无论要求是什么:

Private WithEvents proxy As Sheet1Proxy

Private Sub Class_Initialize()
    Set proxy = New Sheet1Proxy
End Sub

Private Sub proxy_SomeSpecificRangeChanged()
    'business logic to run when SomeSpecificRange is changed
End Sub

问题是代理类与工作表耦合,现在演示者与代理耦合:我们已经抽象了很多东西,但仍然没有办法将工作表/代理依赖项交换为其他东西并测试不涉及工作表的演示者逻辑。

因此,我们创建一个接口来将演示者与代理解耦 - 例如,ISheet1Proxy...现在我们陷入困境,因为我们无法在界面上公开事件。

这就是适配器模式发挥作用的地方,它允许我们形式化“命令”(演示者 -> 视图)和“事件”(视图 -> 演示者)的接口。

使用适配器,工作表/代理和演示者现在完全解耦,现在您可以实现演示者逻辑,而无需任何知识Excel.Worksheet,并且理想情况下任何Excel.Range or Excel.*:每个工作表交互都被形式化为发送到视图/工作表/代理的某些“命令”,或发送到演示者的某些“事件”,就像在战舰项目中一样。

旁注,我发现WeakReference正确拆除对象层次结构并不总是需要这些东西:这就是为什么它在当前版本的战舰代码中不再使用。


显然这是a lot工作的。这是 OOP 原则的绝佳实践,也是学习编写可进行单元测试的解耦代码的绝佳实践……但对于小型 VBA 项目来说,在我看来,这太过分了。


这一切对待Excel.*类作为具体类型,就 VBA 而言,情况也可能如此。但是,那Excel就.NET而言,互操作类型都是接口,所以Rubberduck是即将极大地简化一切 https://github.com/rubberduck-vba/Rubberduck/pull/4681,通过提供包装 APIMoq https://github.com/moq/moq,一个广受欢迎的 .NET 模拟框架:

这将消除将工作表与用户代码完全解耦的需要,以便使其完全可测试 - 唯一的要求是依赖注入,即更喜欢这个:

Public Sub DoSomething(ByVal target As Range)
    target.Value = 42
End Sub

对此:

Public Sub DoSomething()
    Dim target As Range
    Set target = Sheet1.Range("A1")
    target.Value = 42
End Sub
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当工作表的代码隐藏在“代理”接口+类中实现时,正确处理工作表事件 的相关文章

  • 双击事件 - 多个范围

    我正在寻找为双击事件在多个范围内进行编码的最佳方法 Private Sub Worksheet BeforeDoubleClick ByVal Target As Range Cancel As Boolean If Not Interse
  • Mono for Android,读取和写入 XLSX

    我正在使用 Mono for Android 开发一个应用程序 我需要读取和创建 XLSX Excel 文件的功能 我尝试过EPPlus和NPOI 并快速浏览了一下微软的Open XML SDK 发现了以下问题 EPPlus 需要 Wind
  • Excel VBA - 循环文件夹中的文件、复制范围、粘贴到此工作簿中

    我有 500 个包含数据的 Excel 文件 我会将所有这些数据合并到一个文件中 实现此目标的任务列表 我想循环遍历文件夹中的所有文件 打开文件 复制此范围 B3 I102 将其粘贴到活动工作簿的第一张工作表中 重复但在下面粘贴新数据 我已
  • VBA rand 如何使用上限和下限生成随机数?

    所以也许这是多余的 也许这就像问为什么大多数人生来就有 5 个手指 最后的简短答案总是 因为事情就是这样 而且它就是这样工作的 但我讨厌这个答案 该死的我想知道怎么做VBA 中的 Rnd 函数有效 Ms Office Excel 的 MSD
  • 如果字符串包含列表中的单词,则返回与列表值相邻的值

    目前我有一组单元格 每个单元格在无用的信息中都有一个唯一的标识符 我还有这些唯一标识符的列表 以及每个标识符对应的值 我想做的是找到一个单元格包含哪个标识符 如果有的话 然后输出相应的值 下面是一个例子 https i stack imgu
  • 将字段重新格式化为列,其他字段(与先前结构中成为列的字段配对)成为新列中的字段

    我的任务是清理慈善机构设计的移动应用程序中的数据 在一个部分中 用户问答应用程序使用会话由一行表示 该部分由重复的问题答案字段对组成 其中一个字段代表所提出的问题 然后它旁边的字段代表相应的答案 每个问题 字段和答案列对一起代表一个独特的问
  • 如何以编程方式从 Excel 中的 VBA 宏中删除数字签名?

    有没有办法以编程方式从 Excel 工作表的 VBA 宏中删除数字签名 即相当于进入 VBA 编辑器 转到 工具 菜单 gt 数字签名 并单击 删除 的代码 有趣的问题 出于安全原因 它故意不属于 Excel 对象模型的一部分 可以从证书存
  • VBA仅清除数据透视表缓存,但保留数据透视表结构

    如何使用VBA清除数据透视表缓存 但不破坏数据透视表结构 我的数据透视表已连接到外部数据源 SQL 源决定哪个用户应该查看数据的哪一部分 当表刷新时 源会填充该表 我想保存 Excel 文件并使用干净的数据透视表 内部没有数据 分发它 结果
  • 如何每次使用按钮将数据添加到 MATLAB 中的现有 XLSX 文件?

    我有一个函数可以生成一些变量 例如分数 对 错 未回答 使用按钮调用此功能 问题是如何每次将函数生成的这些值添加 附加到 XLSX 文件中 或者 如何创建 MAT 文件以便可以添加它 可能的解决方案是什么 附加到 xls 文件所涉及的挑战是
  • 如何在Java,Apache POI中获取Excel单元格字段的字体样式?

    我想用Java捕获Excel中单元格字段的字体 我正在使用 Apache POI 如果可以的话我想捕捉font color font family font weight font size etc 我怎样才能实现这个目标 根据评论进行编辑
  • 将 copyfromrecordset 写入范围

    我有以下 vba 它从单元格 C10 开始读取 MCO 直到其为空 并将从 SQL 数据库获取机器数量 解密和升级机器数量 这工作正常 但我在获取相应行中的数据时遇到问题 目前它总是将数据写入 D10 因为我已经对其进行了硬编码 但我不确定
  • FileDialog 保留以前的过滤器

    我正在 Access 数据库中制作表单 我需要打开文件对话框窗口几次 我只是不明白为什么在我更改选项值几次并打开文件对话框窗口后它没有更改过滤器 Public Sub Command17 Click Dim fd As FileDialog
  • Range.End() 困惑

    我有一个关于 VBA 中 Range End 属性的一般性问题 我已经阅读了有关该房产的信息here http msdn microsoft com en us library bb221181 aspx 但我还是很困惑 例子 With w
  • SSIS使用列位置而不是名称导入Excel文档

    我想知道是否可以通过按位置引用列来使用 SSIS 导入 Excel 文档 例如 导入列 A D M AA 等 我问这个问题是因为我需要从第三方加载多个 Excel 文档 每个文档在相应的列中包含相同的数据类型 但每个文档的列名称不同 Tha
  • Windows Azure 远程站点“Microsoft.ACE.OLEDB.12.0”提供程序未在本地计算机上注册[重复]

    这个问题在这里已经有答案了 以下代码在我的本地开发计算机上可以正常运行 但是当我部署它时 我收到以下错误消息Azure 远程网站 我已经查看了SO答案和谷歌搜索结果 但我仍然不清楚我必须在本地计算机上安装什么 这样当我将代码推送到Azure
  • IE.navigate2 因保护模式关闭而失败

    我正在从 Excel VBA 自动化 IE8 Excel 2010 Windows 7 Set IE CreateObject InternetExplorer Application IE Navigate2 URL 如果 URL 是处于
  • 需要在Excel中合并3列

    我有 3 列 A B C 我需要合并这 3 列 并且我已经应用了 forumala A1 B1 C1输出为 E 列 我需要输出为 D 列 下面的公式将达到您想要的结果 TEXTJOIN TRUE A1 C1 Textjoin 的工作方式类似
  • 强力查询历年产品利润对比

    我有一个数据集 其中包含公司 产品 利润和年份 公司每年都会销售少量产品并获得利润 公司没有必要在明年销售相同的产品 他们可能会省略以前的产品并添加新的少量产品 我只想对两年的产品进行逐个比较 如下所示 我的数据集是 Company Pro
  • 从受密码保护的 Excel 文件到 pandas DataFrame

    我可以使用以下命令打开受密码保护的 Excel 文件 import sys import win32com client xlApp win32com client Dispatch Excel Application print Exce
  • 你将如何开始自动化我的工作? - 第2部分

    后续这个问题 https stackoverflow com questions 2796128 how would you start automating my job 在经历了第一波进货 9 小时的复制 粘贴 后 我现在相信我已经满足

随机推荐