是否可以将 OfficeJS 命令按钮放置到像 .xlam 文件一样构建的自定义 VBA 插件面板?
* 我仍然希望混合使用 VBA 和 OfficeJS
这尚未经过测试,但我相信按照这些思路可以完成这项工作:
CustomXMLPart.dataNodeReplaced
事件:
function addNodeReplacedEvent() {
Office.context.document.customXmlParts.getByIdAsync("vbaJSBridge", function (result) {
var xmlPart = result.value;
xmlPart.addHandlerAsync(Office.EventType.DataNodeReplaced, function (eventArgs) {
// do stuff with xmlPart
// here you should be able to receive information sent from VBA,
// and return any data necessary.
});
});
}
在VBA中使用:
Dim part As CustomXMLPart
' Returns a custom xml part by its ID:
Set part= ActiveDocument.CustomXMLParts.SelectByID("vbaJSBridge")
part.LoadXML("<data id="vbaJSBridge">some data</data>")
正如我所说,我不确定这是否完全有效,但这是完成这项工作的一个很好的(有点hacky)方法。或者,您可以使用 VBA 设置 HTTP 服务器,您可以将 HTTP 请求发送到 JavaScript。为此,您必须使用 Mswsock.dll 并调用 accept 方法(我认为)。
编辑:
自从我最初的回答以来已经过去了很多个月,现在我确实有了更多的细节。我尝试将
NodeAfterInsert
和 NodeAfterReplace
VBA 事件与 customXmlPart.setXml(sXML);
一起使用,但这不会触发事件。 这是 Microsoft 选择不修复的问题。因此,当前无法进行事件驱动的通信。
在这种情况下,唯一的选择是通过 XML 来轮询 Excel VBA 中的
Application.OnTime()
和 Javascript 中的 setInterval()
更改。
HTTP 服务器仍然是可能的,但至少需要通过 VBA 托管 HTTP 服务器。完全可能(这里是一个 C++ 示例),但需要深入了解 Winsock API 和底层编程。
编辑2
我创建了一个库,它有助于单向消息发送。
'Requires JsBridge from here: https://github.com/sancarn/VbaJsBridge
'ScriptLab test can be downloaded here: https://gist.github.com/sancarn/b974b650f4b451ff2de51861af1671b1
Sub test()
Dim js As JsBridge: Set js = JsBridge.Create("test")
Call js.SendMessageSync("hello world")
Dim col as new collection
For i = 1 to 10
'Add action to a collection so we can check it's status later
col.add js.SendMessage("hello " & i)
next
End Sub
您应该看到我们可以在 JavaScript 控制台中打印“hello world”和“hello 1”...“hello 10”。
将数据从 JavaScript 发送到 VBA 可以使用相同的桥,但具有
for="vb"
属性。然而,这还没有连接起来,请注意,这需要在 VBA 端进行轮询,可能来自工作簿/工作表对象。
不可以,您无法从 Web 插件与 VBA 宏或 COM 插件进行交互。请记住,Web 插件就像网页一样,它们被沙箱化,无法与操作系统或已安装的程序通信(至少在没有帮助程序或代理应用程序/库的情况下)。
所以,这有点老了,但我发现了一种从officeJS 向VBA 发送消息的方法。它依赖于 CustomXMLParts.PartAfterAdd 事件。
本质上在 vba 中创建一个监视器类,类似于::
Public WithEvents parts as CustomXMLParts
Private Sub Class_Initialize
set parts = ActiveWorkbook.CustomXMLParts
End Sub
Private Sub parts_PartAfterAdd(ByVal objPart As CustomXMLPart)
MsgBox("Message received!")
end Sub
Private Sub parts_PartBeforeDelete(ByVal objPart As CustomXMLPart)
MsgBox("Message received!")
end Sub
现在您可以添加或删除 customXML,并且该事件将会触发,但是 officeJS 添加
customXMLPart
的方式有点奇怪。它创建一条空白消息,然后对其进行更改。但事件触发时消息为空。此外,在方法中执行会被冻结,因此在消息非空之前您不能只使用 Application.Wait
。您也不应该从 vba 中删除 CustomXMLPart
,因为这会在 javascript 中引发错误。
您还可以将其与
CustomProperty
结合使用来发送消息,但有注意事项。该值必须介于 1 到 255 个字符之间,0 会导致 OOM 异常,超过 255 的任何内容都将被截断。
我需要这个hacky解决方法从officeJS发送消息来调用一些VBA来执行仅在VBA中可用的功能。由于在事件处理程序完成之前 JavaScript 不会恢复,因此我还可以在同一属性上写一条消息回 OfficeJS 以报告状态或其他内容。
从 VBA 发起通信怎么样?可悲的是,我还没有找到一种非侵入性的方法来做到这一点。
编辑: 有关这些事件的文档非常糟糕,但是
CustomXMLParts.PartAfterLoad
将在填充 XML 后触发。这意味着您可以简化一切,只需监听一个 XML 事件即可将消息发送到 vba。
我在 vba 监视器中包含了一个要点,它将侦听特定名称空间的 xml 消息: https://gist.github.com/mburbea/5300f41173a2eaae477dc4eb7cc69db1