Вырезать и вставить форму Visio в макро

Я пытаюсь написать макрос VBA, который строит базовую диаграмму из данных и определенных шаблонных форм (хранится на отдельной странице). Несмотря на то, что я могу успешно вырезать и вставлять, я, похоже, не могу ссылаться на новую форму после того, как я это сделаю. Я могу переместить форму, прежде чем вырезать и вставить ее, но если я попытаюсь что-либо сделать после этого, я получу ошибку во время выполнения. Существуют различные причины, по которым мне может понадобиться перемещать / обновлять объекты позже, поэтому я должен иметь возможность впоследствии ссылаться на них.

Мой код выглядит следующим образом:

Dim Shape as Visio.Shape
Dim ShapeID as Integer
 
‘copy shape from template page 2, ID 12
Set Shape = Application.ActiveDocument.Pages.ItemU("Page-2").Shapes.ItemFromID(12).Duplicate
 
ShapeID = Shape.ID
MsgBox ("Created shape ID: " & ShapeID)
      
'Now relocate the shape appropriately
currentX = startX + (Count * xSpacing)
currentY = startY
       
Shape.CellsSRC(visSectionObject, visRowXFormOut, visXFormPinX).FormulaForceU = "" & currentX & " mm"
Shape.CellsSRC(visSectionObject, visRowXFormOut, visXFormPinY).FormulaForceU = "" & currentY & " mm"
 
Shape.Cut
   
 'Now go to page 1 and paste the object
 
Application.ActiveDocument.Pages.ItemU("Page-1").Paste

‘*** THE FOLLOWING LINE THAT DOESN’T WORK ***
Set Shape = Application.ActiveDocument.Pages.ItemU("Page-1").Shapes.ItemFromID(ShapeID)
 
Shape.CellsSRC(visSectionObject, visRowXFormOut, visXFormPinX).FormulaForceU = "" & currentX & " mm"
Shape.CellsSRC(visSectionObject, visRowXFormOut, visXFormPinY).FormulaForceU = "" & currentY & " mm"

При выполнении вышеизложенного я получаю ошибку "Неверный идентификатор листа" в выделенной строке (форма успешно вставлена). Если я обрежу эту строку, я получу "исключение произошло" в следующей строке, так что похоже, что я потерял свою ссылку на объект.

3 ответа

Идентификатор фигуры уникален только для ее страницы, поэтому новая фигура, вставленная на страницу 1, получит новый идентификатор и, следовательно, полученную ошибку. Хотя Duplicate метод возвращает ссылку на форму новой формы, Paste не так, вам нужно получить ссылку на него другими способами - либо предположить что-то о выборе окна (согласно ответу Суррогата), либо по индексу:

Dim shp As Visio.Shape
Dim pag As Visio.Page

Set pag = ActivePage 'or some alternative reference to Page-1
Set shp = pag.Shapes.ItemU(pag.Shapes.Count)
Debug.Print shp.Index

Более обычным рабочим процессом будет генерирование мастеров (в документе трафарета), а затем удаление этих мастеров, а не копирование и вставка между страницами, но ваш сценарий может потребовать другого подхода.

Я добавлю эту ссылку как полезную ссылку для работы со свойствами индекса и идентификатора:

[Обновить]

Комментарий Джона Фурнье ниже совершенно прав, что вышесказанное делает предположения. Например, если DisplayLevel ячейка в исходной фигуре меньше самой верхней фигуры, тогда она будет вставлена ​​в коллекцию фигур страницы с соответствующим индексом, поэтому count не вернет правильный идентификатор формы.

Альтернативный подход может заключаться в том, чтобы слушать ShapeAdded событие на страницах (или страница). Ниже приводится небольшая адаптация от IsInScope Пример в документации, с кодом, размещенным ThisDocument. Это позволяет вам добавлять и добавлять код в пару идентификаторов области событий, которую вы можете проверить при обработке события ShapeAdded:

Private WithEvents vPags As Visio.Pages
Private pastedScopeID As Long

Public Sub TestCopyAndPaste()

    Dim vDoc As Visio.Document
    Set vDoc = Me 'assumes code is in ThisDocument class module, but change as required

    Dim srcPag As Visio.Page
    Set srcPag = vDoc.Pages.ItemU("Page-2")

    Dim targetPag As Visio.Page
    Set targetPag = vDoc.Pages.ItemU("Page-1")

    Dim srcShp As Visio.Shape
    Set srcShp = srcPag.Shapes.ItemFromID(12)

    Set vPags = vDoc.Pages

    pastedScopeID = Application.BeginUndoScope("Paste to page")

    srcShp.Copy
    targetPag.Paste

    Application.EndUndoScope pastedScopeID, True

End Sub

Private Sub vPags_ShapeAdded(ByVal shp As IVShape)
    If shp.Application.IsInScope(pastedScopeID) Then
        Debug.Print "Application.CurrentScope " & Application.CurrentScope
        Debug.Print "ShapeAdded - " & shp.NameID & " on page " & shp.ContainingPage.Name
        DoSomethingToPastedShape shp
    Else
        Debug.Print "Application.CurrentScope " & Application.CurrentScope
    End If
End Sub

Private Sub DoSomethingToPastedShape(ByVal shp As Visio.Shape)
    If Not shp Is Nothing Then
        shp.CellsU("FillForegnd").FormulaU = "=RGB(200, 30, 30)"
    End If
End Sub

Я не нашел отличный способ справиться с этим. У меня есть метод, который вставляет буфер обмена на страницу и возвращает все новые фигуры, перечисляя все идентификаторы фигур до и после вставки, а затем возвращая новые фигуры.

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

Конечно, вы получаете ошибку "Неверный идентификатор листа"! Потому что на "Странице-1" вы можете иметь форму с ShapeID, которую вы определили для фигуры, размещенной на "Странице-2".

Вы можете вставить форму и после этого шага определить выбранную форму.

Application.ActiveDocument.Pages.ItemU("Page-1").Paste

' You can define this variable as shape which is selected
Set Shape = Application.ActiveWindow.Selection.PrimaryItem

Почему вы используете переменную два раза?

Вместо Duplicate&Cut&Paste просто используйте Drop:

Dim srcShape, dstShape as Shape
Set srcShape = ActiveDocument.Pages("Page-2").Shapes("srcShape")
Set dstShape = ActiveDocument.Pages("Page-1").Drop(srcShape, 0, 0)

После вышеуказанного вы можете получить доступ dstShape и делайте с ним все, что хотите.

Другие вопросы по тегам