проверить, является ли фигура группой (GroupItems вызывает ошибку) - PullRequest
0 голосов
/ 28 апреля 2018

Я пробовал следующий код в 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?

Ответы [ 3 ]

0 голосов
/ 01 мая 2018

Ошибка проектирования интерфейса

ИМХО, похоже на ошибку дизайна интерфейса. Дизайнеры должны были также экспортировать метод GroupItemsCount, который клиентский код мог бы проверить, прежде чем пытаться вызвать GroupItems.

Ошибки погребения

Мне также не нравится использовать On Error Resume Next (здесь и далее OERN), и в каждой команде разработчиков стараются убедить коллег в том, чтобы они поставили DLL, написанный на .NET (либо на C #, либо на VB.NET), который скрывает все эти ошибки, генерируемые этими методами. .

Метод Excel отсутствует в рабочем листе

Итак, в Excel у нас есть коллекция Worksheets для каждой книги, но дизайнеры забывают экспортировать метод Exists. Нужно вызвать [Worksheets]Item, но если лист не существует, то Item выдает ошибку. Написание собственного метода [Worksheet] Exists в .NET DLL означает, что можно скрыть ошибку и избежать VBA OERN.

SupportsGroupItems

В вашей ситуации я бы порекомендовал изобрести собственную функцию SupportsGroupItems, которая сама имеет обработчик ошибок, но она будет скрыта от просмотра VBA (поэтому ошибки будут скрыты). Он будет размещен в .NET DLL.

Управление

Я понимаю, что многие менеджеры предпочитают чистое решение VBA, и управление может быть напугано до ада DLL, но если вы считаете, что оно одноразовое, их следует убедить. Это будет всего лишь несколько строк кода.

Развертывание

Следующая проблема для решения DLL - это развертывание, не все развертывание выполняется за одно полное развертывание, поэтому вам нужно написать код, чтобы использовать преимущества библиотеки DLL, если она установлена, и откат на VBA, если она не установлена. Вы можете найти в реестре код для определения установки DLL.

Код взаимодействия COM

Если вы прочитали это далеко и уверены, что хотите, чтобы ваш собственный dll хоронил любые ошибки, тогда вам нужен пример кода. Это сообщение в блоге содержит пример кода. Ключом к тому, чтобы сделать .NET DLL вызываемой из VBA, является пометка ComVisible (true) в Assembly.cs и вкладка сборки свойств проекта, чтобы убедиться, что установлен следующий флажок «Регистрация для взаимодействия COM».

Затем обеспечьте разделение интерфейса и класса. Знаешь что? Позвольте мне написать код здесь. Итак, это пример C #, в котором избегает первичной сборки взаимодействия Word с помощью ключевого слова dynamic.

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;
        }
    }
}

Вот пример кода VBA, вызывающего .NET Dll

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

Все вышеперечисленное делает OERN в VBA привлекательным.

0 голосов
/ 08 мая 2018

Пока я получил два очень исчерпывающих ответа, и я благодарен их авторам. Однако я хотел бы избежать On Error ловли, не хотел полагаться на имя фигуры, а также не хотел делать сложные вещи, такие как создание моей собственной dll. Решение Синди с 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
0 голосов
/ 28 апреля 2018

Есть несколько способов сделать это в Word. Первые два, вероятно, также будут работать в Excel, но третий доступен только в Word.

  1. Используйте код, который вы используете с On Error Resume Next и проверьте Err.Number. Если у вас 0, у вас есть группа, если нет, то у вас ее нет.

    Sub CheckIfGroup () Дим ШП Как слово. Форма 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 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

  2. Проверьте WordOpenXML, содержит ли он тег элемента <wpg:wgp> (расшифровывается как wordProcessingGroup, см. Документацию Open XML SDK ). Вы не можете получить WordOpenXML для фигуры, вам нужно запросить его для Shape.Anchor.Paragraphs(1).Range - диапазона в документе Word, с которым связана фигура.

    Sub CheckIfGroup () Дим ШП Как слово. Форма 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
    

    Конец SUb

Обратите внимание, что этот простой подход может работать, только если сгруппированный Shape является единственным привязанным к абзацу. Если их несколько, вы все равно можете использовать WordOpenXML, но вам нужно проанализировать его с помощью инструментов XML, чтобы убедиться, что рассматриваемая форма - это группа.

...