LengthIU Bug Workaround Code
Visio 2003 has a defect in the LengthIU property of a Shape object. This article discusses the bug, describes a work-around, and offers Visual Basic code to help you get back on track!
Note: LengthIU for open paths works properly in Visio 2007 and 2010. This article concerns a problem with Visio 2003.
LengthIU: Just What We Were Looking for!
If you’ve gotten far enough into the Visio Object Model to have discovered the LengthIU shape property, then you are likely quite proud of yourself, and surely ecstatic that you don’t have to calculate path-lengths using any…um…er…MATH
But if you’re using Visio 2003, you might have encountered some strange results which have caused you to distrust this property…and start searching for your trig books. The problem is that the LengthIU property returns 0 for paths that are not closed.
LengthIU is supposed to conveniently returns the total length of the geometry paths in a single shape. If a shape has multiple geometry sections, then the lengths of each section are totaled together.
LengthIU doesn’t iterate through the shapes in a group, however. So don’t be shocked if you get 0 as the LengthIU of a grouped shape. This is not the bug that we’re talking about. For groups, we would just iterate through the sub-shapes and sum all the individual lengths together.
The result that LengthIU returns is always in inches, which is the Internal Unit system that Visio uses for all calculations. Since this IU number is in the scale of the drawing, you could use the result to find out the amount of cabling required for a new networking project, or the distances to nearest fire escapes in a building plan.
A simple example would be to take a 1-inch square and query LengthIU. Of course, we would get 4 as the result. Slightly more complicated, we might draw two such squares, and combine them using: Shape > Operations > Combine. This gives a single shape with two paths. The length of these paths adds up to 8.
As you can see in the illustrations, you can easily test using a snippet of VBA code. First, select a shape in the drawing window. Then hit Alt + F11 to bring up the VBA editing environment. In the VBA Editor’s Immediate window, you can then type this line and hit return:
You should see your result on the next line.
Bugsville: An Open and Closed Issue
However, if we’re measuring distances, we’re probably not measuring closed polygons like circles or rectangles. More likely, we’re measuring connectors or multi-segmented lines, free-form curves, or, god-forbid, NURBS. These shapes are open; they’re non-closed; they have no insides; there ends don’t meet. And that’s where the trouble starts.
In Visio 2003, open paths return a LengthIU = 0.
As you can see; 0, 0, and 0! This is simply a bug. Microsoft has confirmed the bug, but I don’t know if there is any explanation other than “oops!”
Don’t Give Up, There’s Hope!
To get LengthIU to work on open paths, we need to first close them.
It turns out that each Geometry section conveniently has a NoFill section that allows us to do this! Simply setting GeometryN.NoFill = 0 causes a shape to be closed, and LengthIU to return a value other than 0.
But the LengthIU result that we’ll get will then be too long: it will contain the distance from the last vertex to the first vertex, which doesn’t really exist on the path.
So the work-around algorithm goes something like this:
- Determine if a shape has any GeometryN.NoFill = 1 sections
- If not, then just use LengthIU as normal
- For each non-closed geometry section in a shape…
- Close the geometry section using NoFill = 0
- Calculate the distance between the first and last vertex of the section with help from Mr. Pythagoras: SQRT( (x2-x1)^2 + (y2-y1)^2 )
- Increment the sum of all of these distances
- Loop to next geometry section
- Get LengthIU for the altered shape
- Subtract the first-vertex-last-vertex distance sum from that value
- Restore the NoFill cells to their original state
- The LengthIU bug applies to Visio 2003
- Visio 2003 Service Pack 3 (LINK!!) does not fix this problem
- I haven’t tested on Visio 2002 (Edit: it’s a Visio 2003-only bug)
- Visio 2007 has fixed the LengthIU problem (so upgrade!) AMAZON LINK
- There is no ShapeSheet function for calculating LengthIU, it’s a code-only feature. Of course you can always try to derive a trigonometric formula and put it in a User-defined cell…
Below is a listing of the code for the CLengthIuShape class. You can paste the whole thing into a VBA Class Module with the name “CLengthIuShape”.
Note: when cut-and-pasting this code, you might have to do a search and replace on the apostrophe character–to replace the fancy, tilted apostrophe with the simple vertical one, so that VB will understand it.
‘// Class: CLengthIuShape
‘// Constants for ShapeSheet sections, rows, cells:
Private Const Sec_FirstGeo = Visio.VisSectionIndices.visSectionFirstComponent
Private Const Row_FirstVertex = Visio.VisRowIndices.visRowVertex
Private Const Row_Component% = Visio.VisRowIndices.visRowComponent
Private Const Cell_NoFill% = Visio.VisCellIndices.visCompNoFill
Private Const Cell_X = Visio.VisCellIndices.visX
Private Const Cell_Y = Visio.VisCellIndices.visY
‘// Internal class variables:
Private m_visShp As Visio.Shape
Private m_collNonClosedGeoSects As Collection
Private m_extraClosedDistances As Double
Private m_iGeoCt As Integer
Public Function LengthIU(ByVal visShp As Visio.Shape) As Double
m_visShp = visShp
‘// Test for zero geometry sections:
m_iGeoCt = m_visShp.GeometryCount
If m_iGeoCt = 0 Then
LengthIU = 0
‘// Get the closed and non-closed geometry sections:
‘// If there are no non-closed geometry sections, then
‘// LengthIU should function as normal:
If m_collNonClosedGeoSects.Count = 0 Then
LengthIU = m_visShp.LengthIU
‘// If we get this far, then we have non-closed geometry…
Dim lUndoScopeId As Long
Dim varNonClosedSec As Object
m_extraClosedDistances = 0
‘// Since we have non-closed geometry, we will have to temporarily
‘// set the NoFill cells to 1 to get a LengthIU reading. We will
‘// want to undo these actions after we’ve got our calculation, so
‘// we begin an UndoScope here:
lUndoScopeId = m_visShp.Document.BeginUndoScope("LengthIU Fix")
For Each varNonClosedSec In m_collNonClosedGeoSects
LengthIU = m_visShp.LengthIU – m_extraClosedDistances
‘// Now, end the UndoScope, and throw away the changes that we made:
Call m_visShp.Document.EndUndoScope(lUndoScopeId, False)
Private Sub m_alterNonClosedGeoSect(ByVal iSec As Integer)
Dim xR1 As Double, yR1 As Double, xRN As Double, yRN As Double
Dim iLastVertex As Integer
Dim mag As Double
iLastVertex = m_visShp.RowCount(iSec) – 1
‘// Get the values for the first and last vertices:
xR1 = m_visShp.CellsSRC(iSec, Row_FirstVertex, Cell_X).ResultIU
yR1 = m_visShp.CellsSRC(iSec, Row_FirstVertex, Cell_Y).ResultIU
xRN = m_visShp.CellsSRC(iSec, iLastVertex, Cell_X).ResultIU
yRN = m_visShp.CellsSRC(iSec, iLastVertex, Cell_Y).ResultIU
‘// Close the row:
m_visShp.CellsSRC(iSec, Row_Component, Cell_NoFill).ResultIU = 0
‘// Calculate the distance between the first and last point:
mag = Math.Sqr((xRN – xR1) ^ 2 + (yRN – yR1) ^ 2)
‘// Add this distance to the sum of all the manipulated distances:
m_extraClosedDistances = m_extraClosedDistances + mag
Private Sub m_getNonClosedSects()
Dim i As Integer
Dim iSec As Integer
m_collNonClosedGeoSects = New Collection
‘// Find the non-closed geometry sections:
For i = 0 To m_iGeoCt – 1
iSec = Sec_FirstGeo + i
If m_visShp.CellsSRC(iSec, Row_Component, Cell_NoFill).ResultIU <> 0 Then
Private Sub Class_Terminate()
m_visShp = Nothing
m_collNonClosedGeoSects = Nothing
You can also paste the code into a VB.Net class module, but you’ll need to clean up a few items first:
- Add a reference to the Visio type library to your project
- Wrap the code in a Public Class CLengthIuShape – End Class block
- Add the line “Imports Visio = Microsoft.Office.Interop.Visio” to the beginning of the file
- Remove the “Option Explicit” line
- Define the const types with ” As Short” before the equals sign
- Replace “Math.Sqr” with “System.Math.Sqrt” in the m_alterNonClosedGeoSect subroutine
You can test the class against a selected shape using the following code:
Public Sub TestSelectedShape()
‘// See if a shape is selected:
If Visio.ActiveWindow.Selection.Count = 0 Then Exit Sub
‘// Get the selected shape:
Dim shp As Visio.Shape
shp = Visio.ActiveWindow.Selection(1)
‘// Use the new class to calcualte the
‘// length of the shape’s paths:
Dim liuShp As New CLengthIuShape
Dim ln As Double
ln = liuShp.LengthIU(shp) Debug.Print("Length of shape = " & ln & " inches.")