有没有办法使用 VBA 以编程方式更改 Word 2010 中 CanvasShapes 的 Z 顺序位置?

Posted

技术标签:

【中文标题】有没有办法使用 VBA 以编程方式更改 Word 2010 中 CanvasShapes 的 Z 顺序位置?【英文标题】:Is there a way to programmatically change the Z-order position of CanvasShapes in Word 2010 using VBA? 【发布时间】:2021-06-19 03:30:45 【问题描述】:

我在 Word 2010 中渲染 3D 形状,由于我的文档有几个动画图表,我决定将这些形状移动到绘图画布上,每个动画一个。为此,我修改了代码以支持 Shapes 和 CanvasShapes。我已经解决了大多数问题,除了一个问题:Z-Order 方法不会改变 CanvasShapes 的 Z-order 定位。

我一直在互联网上搜索原因,但找不到任何原因。仅使用“ZOrder”和“CanvasShape”这两个搜索词并没有太多意义。我确实找到了 CanvasShape 的 MSDN 类描述,它没有将“Zorder”列为类成员,但是 Zorder 的使用信息将“CanvasShape”列为“支持的类”。如果该方法不改变 z 顺序位置,这意味着什么?

少量的点击让我相信我在做一些完全不合常规的事情或/并且我错过了一些非常基本的事情。

以下是我的测试例程:

Sub Test()
    With ActiveDocument.Shapes.AddCanvas(72, 72, 144, 144)
        .Name = "Test Canvas"
        .CanvasItems.AddShape(msoShapeRectangle, 0, 0, 36, 36).Name = "Shape 1"
        With .CanvasItems.AddShape(msoShapeRectangle, 0, 0, 36, 36)
            .Name = "Shape 2"
            Debug.Print .ZOrderPosition
            .ZOrder msoSendToBack
            Debug.Print .ZOrderPosition
        End With
    End With
End Sub

在 Word VBE 中执行时,将使用两个画布项将绘图画布添加到活动文档中。这个宏会尝试切换两者的 Z 顺序定位。第二个画布形状的 z 顺序位置将在切换尝试之前和之后打印在立即/调试窗口中。如果 zorder 方法正常运行,则之前和之后的值应该不同。在我的系统上,不是因为 zorderposition 没有改变。我还注意到我没有收到错误消息或任何类型的停止。这就是 MSDN 所说的“支持的类”吗?

作为一种解决方法,我考虑剪切 CanvasShapes,然后以正确的 Z 顺序将它们粘贴回画布上,不幸的是,这种方法会破坏我分配的时间片。通过 Word GUI 手动“向前”和“向后发送”的过程仍然适用于这两个类,我将如何模仿?

【问题讨论】:

尝试在您的.ZOrder msoSendToBack 行之后调用DoEvents。 (docs.microsoft.com/en-us/office/vba/language/reference/…) 我尝试使用 DoEvents 进行更改;它对在我的 Word/MSOffice 2010 设置中切换 z 顺序位置没有影响。 【参考方案1】:

休息一个月后,我开始探索一些想法。我首先想到的是,由于“向前”和“向后”按钮仍然适用于绘图画布内的形状,我可以查看按钮的 OnAction 属性中的代码,或者只是简单地调用适当的按钮。

我确实找到了三个 CommandBar,分别命名为“Order”、“Ribbon”和“Selection and Visibility”,我预计它们应该有我需要的这些按钮。不幸的是, OnAction 属性是空的,我没有看到触发任何这些按钮的方法。沮丧的是,我不小心按下了激活功能区的 ALT 键,显示了可用于触发当前可见的各种功能区项目的键盘快捷键。我终于意识到我仍然可以使用按键来选择功能区项目,我认为随着功能区的引入而删除了该功能。我在下面提出了这个原型:一种专门用于绘图画布中的形状的方法,它也适用于绘图画布内部或外部的所有对象。

Sub Zorder(ByRef ShapeObject As Object, ByVal ZorderCmd As MsoZOrderCmd)
    'Add code here, if needed, to preserve last item selected
    ShapeObject.Select
    SendKeys "%", True: SendKeys "P", True
    SendKeys Array("AF", "AE")(ZorderCmd Mod 2) & _
        Mid("RKFBTH", ZorderCmd + 1, 1), True
    'Add code here, if needed, to restore last item selected
End Sub

使用功能区格式化对象需要选择对象,因此会触发 SelectionChange 或 LostFocus 事件,这在某些实现中可能是一个问题,但很容易解决。但是,我在使用 SendKeys 语句时遇到了问题,因为它们要求活动文档是活动的最顶层窗口,在我的场景中,这并不能保证。我最初虽然可以用 SendMessage 或 PostMessage 替换 SendKeys。痛苦的是,我一直在追求这种方法,直到我读到一篇建议不要使用 SendMessage 或 PostMessage 向 word 应用程序发送消息的帖子。

我迷路了。现在怎么办?当我进行最终搜索时,我正要在板上发布另一个问题。不知何故,我进入了 GetVisibleMso 的使用页面,它是 CommendBars 的一个成员函数。该页面讨论了 idMso,解释说功能区上的标准按钮与唯一的 idMso 相关联。它还暗示可以使用它们的 idMso 触发这些按钮。因此,我使用 VBE 中的对象浏览器仔细查看了 CommandBars 类,结果如下: ExecuteMso 方法,idMSO 是其唯一参数;它会触发相应的按钮。

当我使用搜索词“执行功能区项目”时,我得到了比使用触发器或激活更好、更准确的命中率,这可能是我花了一段时间才找到合适解决方案的原因。 Word的标准idMsos列表可以找到here!

有了这个,我的原型的变化如下:

Application.CommandBars.ExecuteMso Array("ObjectBringToFront", _
    "ObjectSendToBack","ObjectBringForward", "ObjectSendBackward", _
    "ObjectBringInFrontOfText", "ObjectSendBehindText")(ZorderCmd)

我读到的其中一个建议是使用 Application.CommandBars。我还发现不需要激活与被触发的功能区按钮关联的选项卡/面板。该按钮只需要可用(换句话说,启用)。

【讨论】:

以上是关于有没有办法使用 VBA 以编程方式更改 Word 2010 中 CanvasShapes 的 Z 顺序位置?的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式打开受密码保护的 VBA 项目

以编程方式添加没有 VBE 或 VBIDE 的 VBA 项目引用..?

MS Word 2013 从 vba 更改图表数据

如何在 VBA 中删除文本 - 以编程方式?

有没有办法以编程方式检查 Entity Framework Core 中的待定模型更改?

有没有办法以编程方式使用kotlin更改片段中的文本颜色?