excel 检查形状是否为组(GroupItems引发错误)

svdrlsy4  于 2023-05-08  发布在  其他
关注(0)|答案(5)|浏览(201)

我在Word中尝试了以下代码:

Sub MyMacro()

Dim sh1 As Shape

For Each sh1 In ActiveDocument.Shapes
 If sh1.GroupItems.Count > 0 Then
  Debug.Print sh1.Name + " is a group!"
  Else: Debug.Print sh1.Name + " is not a group!"
 End If
Next

End Sub

对于实际的分组形状,它可以工作,但是当形状不是一个组时,我得到一个错误:
运行时错误“-2147024891(80070005)”:只能为组访问此成员
除了使用On Error之外,如何检查对象是否是一个组?

7kjnsjlb

7kjnsjlb1#

到目前为止,我在这里得到了两个非常全面的答案,我感谢他们的作者。但是我想避免On Error捕获,不想依赖形状的名称,也不想做复杂的事情,比如构建自己的dll。Cindy使用InStr(rng.WordOpenXML, "<wpg:wgp>")的解决方案看起来是最接近的,但由于某种原因,这段代码在我的文档中不起作用:<wpg:wgp>被发现用于任何形状,分组与否。因此,我决定发布我自己的解决方案,这是非常简单的,似乎对所有情况下正确的工作。我们只需要使用.AutoShapeType属性(@SMeaden在评论中指出的):

Sub MyMacro()

Dim sh1 As Shape

For Each sh1 In ActiveDocument.Shapes
  If sh1.AutoShapeType = msoShapeMixed Then
  Debug.Print sh1.Name + " is a group!"
  Else: Debug.Print sh1.Name + " is not a group!"
 End If
Next

End Sub
eqqqjvef

eqqqjvef2#

在Word中有多种方法可以实现这一点。前两个可能也适用于Excel,但第三个只能在Word中使用。
1.使用On Error Resume Next的代码并检查Err.Number。如果它是0,你有一个组,如果不是,那么你没有。

Sub CheckIfGroup()
    Dim shp As word.Shape
    Dim bIsGroup As Boolean

    Set shp = Selection.ShapeRange(1)

    On Error Resume Next
    Debug.Print shp.GroupItems.Count
    Select Case Err.Number
        Case 0
            bIsGroup = True
        Case -2147024891
            bIsGroup = False
        Case Else
            bIsGroup = False
    End Select
    On Error GoTo 0

    Debug.Print bIsGroup
End Sub

可能还有其他错误,尽管目前我没有想到。
1.另一种可能性是检查Name属性,假设它没有被任何代码更改。默认情况下,它将类似于“组3”,因此

Sub CheckIfGroup()
    Dim shp As word.Shape
    Dim bIsGroup As Boolean

    Set shp = Selection.ShapeRange(1)

    If InStr(shp.NAME, "Group") <> 0 Then
        bIsGroup = True
    Else
        bIsGroup = False
    End If
    Debug.Print bIsGroup
End Sub

1.检查WordOpenXML是否包含元素标记<wpg:wgp>(代表wordProcessingGroup,参见Open XML SDK documentation)。您无法获取Shape的WordOpenXML,您需要查询Shape.Anchor.Paragraphs(1).Range-与Shape关联的Word文档中的Range。

Sub CheckIfGroup()
    Dim shp As word.Shape
    Dim bIsGroup As Boolean
    Dim rng As word.Range

    Set shp = Selection.ShapeRange(1)
    Set rng = shp.anchor.Paragraphs(1).Range
    If InStr(rng.WordOpenXML, "<wpg:wgp>") <> 0 Then
        bIsGroup = True
    Else
        bIsGroup = False
    End If

    Debug.Print bIsGroup
End Sub

请注意,只有当已分组的Shape是唯一锚定到段落的Shape时,这种简单的方法才有效。如果有多个Shape,您仍然可以使用WordOpenXML,但需要使用XML工具对其进行分析,以确保有问题的Shape是属于Group的Shape。

hfyxw5xn

hfyxw5xn3#

我遇到了同样的挑战,发现以前的解决方案要么过于复杂(错误处理),要么不起作用(AutoShapeType)。但是,它显示,通过检查Shape.Type属性,组本身将使用值6(或作为msoGroup)进行标识。
因此,前面的代码可能会调整为以下内容:

Sub MyMacro()

    Dim SHP As SHAPE
    
    For Each SHP In ActiveDocument.SHAPES
        If SHP.Type = 6 Then
            Debug.Print SHP.Name & " is a group!"
        Else
            Debug.Print SHP.Name & " is not a group!"
        End If
    Next

End Sub
q43xntqr

q43xntqr4#

  • 界面设计错误 *

IMHO,它看起来像一个界面设计错误。设计者还应该导出一个GroupItemsCount方法,客户端代码可以在尝试调用GroupItems之前检查该方法。

  • 埋葬错误 *

我也不喜欢使用On Error Resume Next(herefeter OERN),每个开发团队都试图说服同事发布一个用.NET(C#或VB.NET)编写的DLL,它掩盖了这些方法引发的所有错误。

  • Excel缺少Worksheet Exists方法 *

因此,在Excel中,我们为每个工作簿都有一个Worksheets集合,但设计人员忘记了导出Exists方法。必须调用[Worksheets]Item,但如果工作表不存在,则Item抛出错误。在.NET DLL中编写自己的[Worksheet]Exists方法意味着可以隐藏错误并避免VBA OERN。

  • 支持组项目 *

在您的情况下,我建议您创建自己的SupportsGroupItems函数,它本身有一个错误处理程序,但它会隐藏在VBA视图中(这样错误就被隐藏了)。它将被存放在.NET DLL中。

  • 管理 *

我意识到许多经理更喜欢纯VBA解决方案,管理层可能害怕DLL地狱,但如果你告诉他们这是一次性的,他们应该是可以说服的。这将是一个非常少的代码行。

  • 部署 *

DLL解决方案的下一个问题是部署,并不是所有的部署都是在一个完整的推出中完成的,因此如果安装了DLL,您需要编写代码来利用DLL,如果没有安装,则回退到VBA。您可以在注册表中查找代码来检测DLL的安装。

  • COM互操作代码 *

如果你已经读到这里,并确信你想自己的dll来埋葬任何错误,那么你需要一些示例代码。这个blog post有一些示例代码。使.NET DLL可从VBA调用的关键是在Assembly.cs中标记ComVisible(true),并在项目属性的构建选项卡上确保选中以下复选框“注册COM互操作”。
然后确保接口和类的分离。你知道吗?让我在这里写代码。因此,这是通过使用dynamic关键字避免Word主互操作程序集的示例C#。

using System;
using System.Runtime.InteropServices;  // No Word PIA required

namespace SupportsGroupItemsByDynamicLib
{

    public interface ISupportsGroupItemsByDynamic
    {
        bool SupportsGroupItemsByDynamic(object shp);
    }

    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(ISupportsGroupItemsByDynamic))]
    public class CSupportsGroupItemsByDynamic : ISupportsGroupItemsByDynamic
    {
        bool ISupportsGroupItemsByDynamic.SupportsGroupItemsByDynamic(object shp)
        {
            bool bSupportsGroupItems = false;
            try
            {
                dynamic dynaShape = shp;
                dynamic grpShps = dynaShape.GroupItems;
                bSupportsGroupItems = true; // it worked
            }
            catch (Exception)
            {
                // bury the error
            }
            return bSupportsGroupItems;
        }
    }
}

下面是一些调用. NETDll的示例VBA代码

Sub TestByDynamic()
    Dim oGroupItemsByDynamic  As SupportsGroupItemsByDynamic.CSupportsGroupItemsByDynamic
    Set oGroupItemsByDynamic = New SupportsGroupItemsByDynamic.CSupportsGroupItemsByDynamic

    Dim shp As Shape
    For Each shp In ActiveDocument.Shapes

        If oGroupItemsByDynamic.SupportsGroupItemsByDynamic(shp) Then
            Debug.Print shp.Name + " is a group! and has " & shp.GroupItems.Count & " items"
        Else
            Debug.Print shp.Name + " is not a group!"
        End If
    Next

End Sub

所有这些都使VBA中的OERN看起来很可口。

cuxqih21

cuxqih215#

虽然它们看起来是矛盾的,但两个答案都是正确的,所以这里要澄清的是Shape的属性GroupItems被示例化并可以查询的所有情况:

  • 有一些形状可以通过它们的性质Type = msoGroup(6)来识别。这些形状是通过组合2个或更多其他形状而创建的。
  • 有一些形状可以通过它们的性质AutoShapeType = msoShapeMixed(-2)来识别。这些是对应于SmartArt Graphics的形状。

请注意,问题是关于Excel和Word的,但答案也适用于PowerPoint。
这个答案是社区维基,以便在需要时修复它,或者在存在其他可能性时完成它。

相关问题