使用vba在Excel中对形状进行分组和命名

时间:2012-08-17 09:20:43

标签: excel vba excel-vba shapes

在Excel vba中,我使用vba在excel中创建两个形状。一个箭头,我将其命名为“aro”+ i,以及一个文本框,我将其命名为“text”+ i,其中i是一个表示照片编号的数字。

所以,对于照片3,我会创建箭头“aro3”和文本框“text3”。

然后我想将它们分组并将该组重命名为“arotext”+ i,在这种情况下重命名为“arotext3”。

到目前为止,我一直在进行分组和重命名:

targetSheet.shapes.Range(Array(Arrow.Name, textBox.Name)).Select
Selection.group
Selection.Name = "AroTxt" & Number

在sub中工作得非常好,但现在我想把它改成一个函数并返回命名组,所以我试过这样的事情:

Dim arrowBoxGroup as Object
set arrowBoxGroup = targetSheet.shapes.Range(Array(Arrow.Name, textBox.Name))
arrowBoxGroup.group
arrowBoxGroup.Name = "AroTxt" & Number

当我创建一个与已经创建的名称相同的新组时,我遇到了问题。因此,如果我创建第二个“aro3”和“text3”,然后尝试对它们进行分组并将该组重命名为“arotext3”,则会出现错误,因为已经存在具有相同名称的组。

我不明白的是,当我使用引用选择的方法执行此操作时,如果我愿意,我可以重命名每个具有相同名称的组,并且不会出现错误。为什么它在引用Selection对象时有效,但在尝试使用已分配的对象时失败?

更新:

因为有人问,我到目前为止的代码如下。箭头和文本框是一个箭头和一个文本框,指向用户使用表单任意定义的方向。

然后在目标工作表上以正确的角度创建一个箭头,并在箭头的末尾放置一个具有指定数字(也通过表单)的文本框,以便它有效地形成一个标注。我知道有标注,但他们没有做我想要的,所以我必须自己制作。

我必须对文本框和箭头进行分组,因为1)它们属于一起,2)我使用组的名称作为参考来跟踪已​​经放置了哪些标注,3)用户必须将标注放在右侧位于工作表中的地图上的位置。

到目前为止,我已经设法通过将返回值设为GroupObject来使其成为函数。但是这仍然依赖于Sheet.Shapes.range()。选择,在我看来这是一个非常糟糕的方法。我正在寻找一种不依赖于选择对象的方法。

我想了解为什么这在使用选择时有效,但在使用强类型变量来保存对象时失败。

    Public Function MakeArrow(ByVal No As Integer, ByVal angle As Double, ByVal size As ArrowSize, ByVal ArrowX As Double, ByVal ArrowY As Double, ByVal TargetInternalAngle As Double, ByRef targetSheet As Worksheet) As GroupObject

    Dim Number As String
    Dim fontSize As Integer
    Dim textboxwidth As Integer
    Dim textboxheight As Integer
    Dim arrowScale As Double
    Dim X1 As Double
    Dim Y1 As Double
    Dim X2 As Double
    Dim Y2 As Double
    Dim xBox As Double
    Dim yBox As Double
    Dim testRange As Range
    Dim arrow As Shape
    Dim textBox As Shape
'    Dim arrowTextbox As ShapeRange
'    Dim arrowTextboxGroup As Variant

    Select Case size
        Case ArrowSize.normal
            fontSize = fontSizeNormal
            arrowScale = arrowScaleNormal
        Case ArrowSize.small
            fontSize = fontSizeSmall
            arrowScale = arrowScaleSmall
        Case ArrowSize.smaller
            fontSize = fontSizeSmaller
            arrowScale = arrowScaleSmaller
    End Select
    arrowScale = baseArrowLength * arrowScale

    'Estimate required text box width
    Number = Trim(CStr(No))
    Set testRange = shtTextWidth.Range("A1")
    testRange.value = Number
    testRange.Font.Name = "MS P明朝"
    testRange.Font.size = fontSize
    shtTextWidth.Columns(testRange.Column).EntireColumn.AutoFit
    shtTextWidth.Columns(testRange.row).EntireRow.AutoFit
    textboxwidth = testRange.Width * 0.8
    textboxheight = testRange.Height * 0.9
    testRange.Clear

    'Make arrow
    X1 = ArrowX
    Y1 = ArrowY
    X2 = X1 + arrowScale * Cos(angle)
    Y2 = Y1 - arrowScale * Sin(angle)
    Set arrow = AddArrow(X1, Y1, X2, Y2, Number, targetSheet)

    'Make text box
    Set textBox = Addtextbox(angle, Number, fontSize, X2, Y2, textboxwidth, textboxheight, TargetInternalAngle, targetSheet)

    'Group arrow and test box
    targetSheet.shapes.Range(Array(arrow.Name, textBox.Name)).group.Select
    Selection.Name = "AroTxt" & Number

    Set MakeArrow = Selection

'    Set arrowTextbox = targetSheet.shapes.Range(Array(arrow.Name, textBox.Name))
'    Set arrowTextboxGroup = arrowTextbox.group
'    arrowTextboxGroup.Name = "AroTxt" & Number
'
'    Set MakeArrow = arrowTextboxGroup

End Function

Private Function AddArrow(ByVal StartX As Double, ByVal StartY As Double, ByVal EndX As Double, ByVal EndY As Double, ByVal Number As String, ByRef targetSheet As Worksheet) As Shape

    Set AddArrow = targetSheet.shapes.AddLine(StartX, StartY, EndX, EndY)
    With AddArrow
        .Name = "Aro" & Number
        With .Line
            .BeginArrowheadStyle = msoArrowheadTriangle
            .BeginArrowheadLength = msoArrowheadLengthMedium
            .BeginArrowheadWidth = msoArrowheadWidthMedium
            .ForeColor.RGB = RGB(0, 0, 255)
        End With
    End With

End Function

Private Function Addtextbox(ByVal angle As Double, ByVal Number As String, ByVal fontSize As Integer, ByVal arrowEndX As Double, ByVal arrowEndY As Double, ByVal Width As Integer, ByVal Height As Integer, ByVal LimitAngle As Double, ByRef targetSheet As Worksheet) As Shape

    Dim xBox, yBox As Integer
    Dim PI As Double
    Dim horizontalAlignment As eTextBoxHorizontalAlignment
    Dim verticalAlignment As eTextBoxVerticalAlignment

    PI = 4 * Atn(1)

    If LimitAngle = 0 Then
        LimitAngle = PI / 4
    End If

    Select Case angle
        'Right
        Case 0 To LimitAngle, 2 * PI - LimitAngle To 2 * PI
            xBox = arrowEndX
            yBox = arrowEndY - Height / 2
            horizontalAlignment = eTextBoxHorizontalAlignment.left
            verticalAlignment = eTextBoxVerticalAlignment.Center
        'Top
        Case LimitAngle To PI - LimitAngle
            xBox = arrowEndX - Width / 2
            yBox = arrowEndY - Height
            horizontalAlignment = eTextBoxHorizontalAlignment.Middle
            verticalAlignment = eTextBoxVerticalAlignment.Bottom
        'Left
        Case PI - LimitAngle To PI + LimitAngle
            xBox = arrowEndX - Width
            yBox = arrowEndY - Height / 2
            horizontalAlignment = eTextBoxHorizontalAlignment.Right
            verticalAlignment = eTextBoxVerticalAlignment.Center
        'Bottom
        Case PI + LimitAngle To 2 * PI - LimitAngle
            xBox = arrowEndX - Width / 2
            yBox = arrowEndY
            horizontalAlignment = eTextBoxHorizontalAlignment.Middle
            verticalAlignment = eTextBoxVerticalAlignment.top
    End Select

    Set Addtextbox = targetSheet.shapes.Addtextbox(msoTextOrientationHorizontal, xBox, yBox, Width, Height)
    With Addtextbox
        .Name = "Txt" & Number
        With .TextFrame
            .AutoMargins = False
            .AutoSize = False
            .MarginLeft = 0#
            .MarginRight = 0#
            .MarginTop = 0#
            .MarginBottom = 0#
            Select Case verticalAlignment
                Case eTextBoxVerticalAlignment.Bottom
                    .verticalAlignment = xlVAlignBottom
                Case eTextBoxVerticalAlignment.Center
                    .verticalAlignment = xlVAlignCenter
                Case eTextBoxVerticalAlignment.top
                    .verticalAlignment = xlVAlignTop
            End Select
            Select Case horizontalAlignment
                Case eTextBoxHorizontalAlignment.left
                    .horizontalAlignment = xlHAlignLeft
                Case eTextBoxHorizontalAlignment.Middle
                    .horizontalAlignment = xlHAlignCenter
                Case eTextBoxHorizontalAlignment.Right
                    .horizontalAlignment = xlHAlignRight
            End Select
            With .Characters
                .Text = Number
                With .Font
                    .Name = "MS P明朝"
                    .FontStyle = "標準"
                    .size = fontSize
                    .Strikethrough = False
                    .Superscript = False
                    .Subscript = False
                    .OutlineFont = False
                    .Shadow = False
                    .Underline = xlUnderlineStyleNone
                    .ColorIndex = xlAutomatic
                End With
            End With
        End With
        .Fill.Visible = msoFalse
        .Fill.Solid
        .Fill.Transparency = 1#
        With .Line
            .Weight = 0.75
            .DashStyle = msoLineSolid
            .style = msoLineSingle
            .Transparency = 0#
            .Visible = msoFalse
        End With
    End With


End Function

3 个答案:

答案 0 :(得分:6)

Range.Group返回一个值。你可以试试:

Set arrowBoxRange = targetSheet.shapes.Range(Array(Arrow.Name, textBox.Name))
Set arrowBoxGroup = arrowBoxRange.Group
arrowBoxGroup.Name = "AroTxt" & Number

我怀疑当前的选择更新,就像您之前的工作中的以下内容一样:

Set Selection = Selection.Group  'it's as if this is done for you when you create the group.

导致差异。

仅供参考,我使用的是Excel 2010,无法复制基于Selection的原始代码片段(我收到错误“Selection.Name =”,这会使对象不支持属性。)

好的,我可以让这个工作:

Selection.Group.Select
Selection.Name = "AroTxt"

当然,就像我建议的其他代码片段一样,这会重新分配组的返回值,因此Selection.Group和Selection.Name中的Selection指的是不同的对象,我认为这就是你想要的。

答案 1 :(得分:0)

这是因为您出现此错误时手动将新组存储为对象。您可能无法对 “AroTxt”&的多个实例执行任何操作。您已创建的号码 。因为excel无法决定你的意思。

Excel不应该允许这样但它并不总是警告这已发生但如果您尝试选择具有重复名称的组则会出错。

即使不是这种情况,重复变量名也不是好习惯。将额外的Arrow和textBox添加到组中会不会更好?

因此,要解决您的问题,您必须在保存之前检查该组是否已存在。如果存在,可以将其删除或添加到组中。

希望这有帮助

答案 2 :(得分:0)

编辑:因为它似乎总是如此,我点击提交后错误开始弹出。我会稍微修补一下,但会回应@royka,想知道你是否真的需要给多个形状赋予相同的名称。

以下代码似乎可以满足您的需求(创建形状,给它们命名,然后分组)。在分组功能中,我将“AroText”编号保持不变只是为了查看是否会发生错误(它没有)。似乎两种形状都具有相同的名称,但它们的区别在于它们Shape.ID。从我所知道的,如果你说ActiveSheet.Shapes("My Group").Select,它将选择具有最低ID的那个名称的元素(至于为什么它允许你将两个名称命名为相同的名称,没有线索:)。

这不是你的“为什么”(我无法复制错误)的问题的答案,但这有望为你提供一种“如何”的方式。

Sub SOTest()

Dim Arrow As Shape
Dim TextBox As Shape
Dim i as Integer
Dim Grouper As Variant
Dim ws As Worksheet

Set ws = ActiveSheet

' Make two shapes and group, naming the group the same in both cases
For i = 1 To 2
  ' Create arrow with name "Aro" & i
  Set Arrow = ws.Shapes.AddShape(msoShapeRightArrow, 10, 50, 30, 30)
  Arrow.Name = "Aro" & i

  ' Create text box with name "Text" & i
  Set TextBox = ws.Shapes.AddTextbox(msoTextOrientationHorizontal, 50, 50, 40, 40)
  TextBox.Name = "Text" & i

  ' Use a group function to rename the shapes
  Set Grouper = CreateGroup(ws, Arrow, TextBox, i)

  ' See the identical names but differing IDs
  Debug.Print "Name: " & Grouper.Name & " | ID: " & Grouper.ID
Next

End Sub


Function CreateGroup(ws As Worksheet, Arrow As Shape, TextBox As Shape, Number As Integer) As Variant

Dim arrowBoxGroup As Variant

' Group the provided shapes and change the name
Set arrowBoxGroup = ws.Shapes.Range(Array(Arrow.Name, TextBox.Name)).Group
arrowBoxGroup.Name = "AroTxt" & Number

' Return the grouped object
Set CreateGroup = arrowBoxGroup

End Function