使用VBA将标签与Excel流程图中的箭头匹配

时间:2013-12-03 23:33:44

标签: excel vba excel-vba charts

我正在使用Excel中的VBA编写代码生成工具(不要问为什么长篇故事)。我需要能够“解析”流程图。

问题是Excel允许形状包含文本,连接器除外:线条和箭头不能包含文本。要标记箭头,您只需在其上放置一个文本框 - 但该框不会以VBA可以轻松捕获的方式“附加”到箭头上。

例如,用户可能会画出如下内容:

example flowchart: three boxes, two arrows, two more boxes as arrow labels

在我的VBA代码中,我可以使用ActiveSheet.Shapes来查找流程图包含七个形状:有五个框(两个标签只是没有边框的框)和两个箭头。然后Shape.TextFrame2会告诉我每个方框内写的是什么,Shape.ConnectorFormat会告诉我每个箭头的开头和结尾都有哪个方框。

我需要的是可以推断的代码:

  • 标签A属于从方框1到方框2的箭头
  • 标签B属于第1栏至第3栏的箭头

我可以想到三种做法,但都不令人满意。

  1. 要求用户将每个标签与相应的箭头分组。

  2. 然后找出每个箭头的终点坐标 计算哪些箭头通过哪些标签。

  3. 找出每个方框角落的坐标,然后计算 哪些标签位于哪一对盒子之间。

  4. 方法1使程序员更容易,但对用户来说更难。它为用户错误开辟了很多潜力。我认为这不是一个可以接受的解决方案。

    方法2相当容易实现,除了我不知道如何找到坐标!

    方法3是可行的(Shape.Left等将给出坐标)但计算上相当混乱。它也有可能产生歧义(取决于位置,相同的标签可能与多个箭头相关联)。

    请注意,方法2和方法3都涉及尝试将每个标签与每个箭头匹配:复杂性是二次的。典型的应用将有10-50个箭头,所以这种方法是可行的,如果有点不优雅。

    有没有人有更好的主意?理想情况下,它不会涉及坐标几何和复杂逻辑,也不会要求用户改变绘制流程图的方式。


    编辑添加:示例2以回应Tim Williams

    flowchart with one box, two arrows, and a label whose bounding box intersects the bounding boxes of both arrows

    这是一个标签,其边界框与两个箭头的边界框相交,其中点不在任一箭头的边界框内。在视觉上,人类很容易看到它属于左箭头,但是以编程方式很难处理。如果我能找到箭头端点的坐标,那么我可以计算出一个箭头穿过标签的盒子而另一个箭头没有。但是,如果我只有箭头的边界矩形,那么它就不起作用了。

2 个答案:

答案 0 :(得分:1)

有趣的问题。如果您考虑箭头所涵盖的范围以及文本框所涵盖的范围并根据最重叠进行匹配,该怎么办。

Sub ListShapes()

    Dim shp As Shape
    Dim shpArrow As Shape
    Dim vaArrows As Variant
    Dim i As Long
    Dim rIntersect As Range
    Dim aBestFit() As String
    Dim lMax As Long

    vaArrows = Split("Straight Arrow Connector 7,Straight Arrow Connector 9", ",")
    ReDim aBestFit(LBound(vaArrows) To UBound(vaArrows))

    For i = LBound(vaArrows) To UBound(vaArrows)
        Set shpArrow = Sheet1.Shapes(vaArrows(i))
        lMax = 0

        For Each shp In Sheet1.Shapes
            If shp.Name Like "Label*" Then
                Set rIntersect = Intersect(Sheet1.Range(shp.TopLeftCell, shp.BottomRightCell), _
                    Sheet1.Range(shpArrow.TopLeftCell, shpArrow.BottomRightCell))

                If Not rIntersect Is Nothing Then
                    If rIntersect.Count > lMax Then
                        lMax = rIntersect.Count
                        aBestFit(i) = shp.Name
                    End If
                End If
            End If
        Next shp
    Next i

    For i = LBound(vaArrows) To UBound(vaArrows)
        Debug.Print vaArrows(i), aBestFit(i)
    Next i

End Sub

我用五盒二箭头设置测试了这个,没有什么比这更复杂了。我将两个箭头放在一个数组中,但我认为你有办法识别箭头。我还将我的不受限制的盒子命名为“标签x”,这样我就能识别它们,但我再次假设你有更复杂的东西。

代码遍历每个箭头。在该循环内部,它遍历每个形状。如果它是标签,则它计算两个范围交叉处的单元格。无论哪个具有最多的存储在最合适的阵列中。

如果你有一个合理的流程图表来测试这个以查看陷阱的位置,那将是很好的。我认为这不一定比使用坐标更好,只是采用不同的方法。

答案 1 :(得分:1)

您可以按如下方式找到箭头端点的坐标。

首先,正如蒂姆·威廉姆斯指出的那样,.Left.Top.Width.Height属性描述了箭头的边界矩形。

接下来,检查.Horizo​​ntalFlip和.VerticalFlip属性。如果两者都为假,则箭头在其边界矩形中从左上角到右下角。也就是说,箭头的开头有坐标(.Left,.Top),结尾有坐标(.Left+.Width,.Top+.Height)

如果* .Flip为真,则需要根据需要交换坐标。例如,如果.HorizontalFlip为真但.VerticalFlip为假,则箭头从(.Left+.Width,.Top)运行到(.Left,.Top+.Height)

据我所知,MSDN上的任何地方都没有记录。感谢Andy Pope在excelforums.com上提到它。

鉴于此,方法2似乎是最好的方法。