Share:

" /> Visio Guy » LengthIU Bug Workaround Code
Home » Code

LengthIU Bug Workaround Code

Submitted by on October 20, 2007 – 12:14 pm | | 18427 views 10 Comments

Read Full Article

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 Background

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.

LengthIU Examples

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:

? Visio.ActiveWindow.Selection(1).LengthIU

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

Closing Points

  • 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…

The Cure

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

Option Explicit

'// 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

    Exit Function

  End If

  '// Get the closed and non-closed geometry sections:
  Call m_getNonClosedSects()

  '// If there are no non-closed geometry sections, then
  '// LengthIU should function as normal:

  If m_collNonClosedGeoSects.Count = 0 Then

    LengthIU = m_visShp.LengthIU

    Exit Function

  End If

  '// 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

    Call m_alterNonClosedGeoSect(CInt(varNonClosedSec))

  Next

  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)

End Function

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

End Sub

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

      Call m_collNonClosedGeoSects.Add(iSec)

    End If

  Next

End Sub

Private Sub Class_Terminate()

  m_visShp = Nothing
  m_collNonClosedGeoSects = Nothing

End Sub

You can also paste the code into a VB.Net class module, but you’ll need to clean up a few items first:

  1. Add a reference to the Visio type library to your project
  2. Wrap the code in a Public Class CLengthIuShape – End Class block
  3. Add the line “Imports Visio = Microsoft.Office.Interop.Visio” to the beginning of the file
  4. Remove the “Option Explicit” line
  5. Define the const types with ” As Short” before the equals sign
  6. 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.")

End Sub

10 Comments »

  • al edlund says:

    Chris,
    this was one that showed up in 2003,
    al

  • Visio Guy says:

    Thanks Al!

    It’s a 2003-only bug folks!

  • Bryan says:

    I have been looking for something like this for Visio 2003, but when I created the class, as per your directions, I kept getting errors, both compile time and run-time, the most common of which is: ‘Object variable or With block variable not set.

  • Hi Chris,

    Note that starting in Visio 2003, the LengthIU and AreaIU properties take an argument indicating whether sub-shapes should be included. Also note that there are special exclusions for Data Graphic callouts as mentioned in the Visio 2007 help topics.

    http://msdn2.microsoft.com/en-us/library/ms196113.aspx

    Mark

  • Visio Guy says:

    Bryan,

    Are your quote marks the simple ones or the 66 99 quote marks? I noticed a problem when cutting from the blog and pasting in the VBA editor…

    – Chris

  • Visio Guy says:

    Mark

    Thanks for the tip on the arguments, don’t know how I missed those!

    And the info in the link is cool. Foks, it looks like that in general, Data Graphic sub-shapes won’t be included in the LengthIU and AreaIU calculations! See Mark’s link above for more info!

    – Chris

  • John says:

    This looks great, but I can’t quite get it to work. I get the old error 91, ‘Object variable or with block variable not set’ error here:

    shp = Visio.ActiveWindow.Selection(1)

    A few debugs show that there is an instance, and it can be assigned to shp if that is a Variant. But no joy if “Dim shp As Visio.Shape”

    Any tips appreciated.

  • neilkeron says:

    John
    I cant quite get it to work either.
    If I change the statement shp = visio.activewindow.selction(1) to Set shp =visio.activewindow.selection(1), I get past the first error but then I get error 91 at ln = liuShp.LengthIU(shp), presumably because something has gone wrong in the function. If anyone can throw any light on this I would be grateful.

    Regards
    Neil Keron

  • stodds says:

    Greetings,

    Were the issues raised here ever sorted? I am having trouble getting this to work too and not having used VB before ain’t helping!

    Cheers

    Steve

  • Visio Guy says:

    LengthIU on open paths works properly in Visio 2010.

Leave a comment!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.

*