Hello,
I'm a relative Visio newbie, thrust into the environment for reasons of cost savings. Overall, I'm making the transition fairly well but, like any good engineer my ultimate goal is to become really, really lazy. To that end, I'll expend great amounts of effort to get there!
We are currently using Visio13 for electrical system schematics. 90% or greater of our drawings are for interconnections black boxes together with wires and cables. The Powers-that-Be have determined that our previous CAD package was too expensive to maintain, and that Visio would suit our needs at a fraction of the cost. Mostly, they are correct.
Now to the crux of my problem.
In our drawings, we are often having to resize shapes (i.e. electrical connectors). The default behavior in Visio tends to be that the connection points move based on proportion of the shape rather than on grid or with defined spacing.
I would like to build/create a smart shape that started off as two pins, but that the user can stretch or collapse as required in their drawing, while automatically inserting/removing connection points corresponding to the grid. I've attached a sheet to hopefully demonstrate more precisely the desired effect.
This thread (http://visguy.com/vgforum/index.php?topic=8087.0) seems to maybe cover the collapsing behavior to some extent, but I'm not sure that's the right approach for what I need.
Any help would be greatly appreciated!
Here are some things to help you on your way...
http://visguy.com/vgforum/index.php?topic=6314.msg31942#msg31942 (http://visguy.com/vgforum/index.php?topic=6314.msg31942#msg31942)
See attached for "expanding/contracting" shapes.
Wapperdude
I have spent an awful lot of time not getting very far. I think my trouble is truly understanding how the properties in teh shape sheets interact.
So, perhaps I need to back off a bit and start more simply.
Looking at the VSD in my OP, I think I'd just like to start simply with being able to stretch a shape and have it preserve some certain dimensions. See C & D in that VSD.
I'll then build from there.
Are there any decent tutorials somewhere that are specific to just being able to create a 'stretchable' shape that preserves certain dimensions? Like maybe with an arrow that preserves the size and ratios of the arrow head when stretched?
Sorry about the confusion. Attached is a simpler shape. This demonstrates the basics of stretching a shape between some minimum value and maximum value in discrete steps using the bound function. As the shape expands or contracts, connection points are "added" or removed" as is appropriate. Note, no connection points are eliminated, the unused ones are merely stacked on top of each other. It would require code to reduce or increase the number of connection points as that would require eliminating or adding lines to the Connection point section of the shapesheet.
HTH
Wapperdude
Here's another version, this uses a fixed list to choose the number of connection points. Dbl click the shape to bring up the shapedata, select the number of connection points, and enter the desired pin to pin spacing. The shape will automatically size based upon those settings.
Wapperdude
I'm back, and I've not been able to accomplish any of my desired traits in this thread. So I'm going to back way off to baby steps.
Step 1.
I would like to build a shape in Visio, and when I stretch it or contact it, I want both ends of the shape to maintain their dimensions. At this point I am not at all worried about connection points. I just want the shape to act as a 'smart shape' as in this article (http://www.visguy.com/2009/07/22/why-visio-shape-smarts-makes-your-life-easier/). I can stretch the arrow as much as I want, but the arrow head itself stays the same. I want to learn how to create this functionality for my shapes (that aren't arrows).
What properties allow me to do this? Where in the ShapeSheet or elsewhere do I need to go to make this happen? The Shape Design --> Behavior or Shape Design --> Protection dialogs seem like they should be helpful, but they are not.
I tried using the arrow stencil from General --> Blocks, and opening the ShapeSheet, but I can't seem to find anything that 'locks' the arrowhead dimensions in place.
Please help!
Would this help: http://visguy.com/vgforum/index.php?topic=7120.0 ?
Wrt shape stretching, this article directly covers the topic: https://msdn.microsoft.com/en-us/library/aa200966(v=office.10).aspx (https://msdn.microsoft.com/en-us/library/aa200966(v=office.10).aspx). It is part of a very comprehensive discussion based upon V2002. Everything in this entire article, some 28 chapters, is excellent foundational information.
Enjoy!
Wapperdude
Yes, both of these posts are VERY helpful!
So, thanks to the posts on the 12th, I have made some great progress! I have been able to use the Geometry section to create a smart shape with the behavior I would like.
Now, on to the connection points. I've attached a vsd with the shape I've created, and I've added a few connection points that behave as I would like.
Now, I'm looking to add the connection points automatically.
I'm fairly adept at writing excel functions, and it seems that Visio uses a lot of the same functions, but even more.
I'm thinking there ought to be a way to add connection points automatically based on a function of the height. Perhaps test for height, and divide by distance, and the result is N. For every N, place a connection point. Am I barking up the right tree?
Take a peek at what I have so far and feel free to critique.
I'm getting very much closer, but it appears that I need to run a Macro to get the connection points to work properly. I haven't written code in ages, and I'm not sure how to call aspects of the shape sheet into Visual Basic, so I would appreciate any help in that arena. I think my logic is right, or at least right enough to get where I want to go, but syntax is where I'm stuck.
Sub AddConnPts()
Dim I As Integer 'Incrementer
Dim N As Integer 'Number of Connection Points Needed
Dim C As Integer 'Number of existing Connection Points
Dim H As Long 'Height of Shape
Dim HString As String 'String to put in Connect points Y Column
Dim WString As String 'String to put in Connect points X Column
Get Shape NameID 'Don't know how to do this
Get shape Height = H 'How to get height off shapesheet for NameID
If H > 0.5 Then
N = (H - 0.25) / 0.125 'Sets number of needed connection points based on geometry of shape
Get Existing Connection Points = C 'How to count the number of existing connection points on NameID
If C < N Then
For I = C + 1 To N
HString = "IF(Height*1>=0.5,Height*1-0.125*" &I ",0" 'how do I put this in the Connection Points area of Shape Sheet?
WString = "Width*1"
'How to insert strings in next incremented row of Connection Points section of shape sheet?
Next I
End If
End If
End Sub
My intention is to add an Actions section into the part shape sheet and create an "Add Connections" Menu option for the shape that would then run the macro.
Any and all help is greatly appreciated!
Hi Walrus,
I had a look at your request and started working out a solution, for what you exactly asked for, then I understood that you were not that much after the macro itself, but rather the final shape (scaling as needed and moving its connection points) and finally I recalled having seen these exact shapes in an old topic.
Have a look at it: http://visguy.com/vgforum/index.php?topic=6271.15
It probably doesn't follow exactly your strategy, but I remember that Rudy was happy with his final result.
HTH, Y.
Thanks, Yacine.
I looked at the implementation you linked, and while similar, that direction won't really work for our implementation. I also wonder about the efficiency of them in larger drawing packages. I've noticed that the larger a multi-page drawing is, the more Visio behaves...slowly. So I wonder if huge, complicated shape sheets attached to every shape would affect performance?
I've also decided to go the macro direction for portability. It seems to me that I should be able to write one macro to perform the necessary connection point automation across multiple shapes.
It's also now become...personal. I want to conquer this.
In my research over the past few days, I've found two links that appear to give me the tools I need to conquer this issue. One is yours, driven by the link above I got to this one (http://visguy.com/vgforum/index.php?topic=5802.msg23255#msg23255), which solves my problem of how to apply the macro to any selected shape.
The other is an MSDN article (https://msdn.microsoft.com/en-us/vba/visio-vba/articles/shape-cells-property-visio) that helps me add cells to the shape sheet and populate those cells. This is also the link that makes me believe I can use a generic macro across multiple shapes.
If I am uninterrupted today, I believe I can have a working model today that should provide the functionality I want. It might need tweaking, but I think I can get there from here.
So, I didn't have long to work on this today and I'm making progress, but my code is behaving funny.
Sub AddCnPts()
'
' Automatically Add Connection Points
'
Dim UndoScopeID2 As Long
UndoScopeID2 = Application.BeginUndoScope("Add Connect")
Dim intRowIndex1 As Integer
intRowIndex1 = ActiveWindow.Selection(1).AddRow(visSectionConnectionPts, visRowLast, visTagCnnctPt)
Dim vsoRow1 As Visio.Row
Dim vsoCell As Visio.Cell
Dim vsoShape As Visio.Shape
Dim strHRow As String
Dim shpHgt As Long
Dim intI As Integer
Set vsoShape = ActiveWindow.Selection(1)
Set vsoCell = vsoShape.CellsU("Height")
shpHgt = vsoCell
If shpHgt >= 0.5 Then
intI = (shpHgt - 0.25) / 0.125 'Sets number of needed connection points based on geometry of shape
End If
strHRow = "=IF(Height*1>=0.5,Height*1-(0.125*" & intI & "),0)"
Set vsoRow1 = ActiveWindow.Selection(1).Section(visSectionConnectionPts).Row(intRowIndex1)
vsoRow1.Cell(visCnnctX).FormulaU = "Width*1"
vsoRow1.Cell(visCnnctY).FormulaU = strHRow
vsoRow1.Cell(visCnnctDirX).FormulaU = -1#
vsoRow1.Cell(visCnnctDirY).FormulaU = 0#
vsoRow1.Cell(visCnnctType).FormulaU = visCnnctTypeInward
Application.EndUndoScope UndoScopeID2, True
End Sub
This is just a starting point to make sure I can access individual cells and actually write the connection point data. That stuff works.
However, when I set a breakpoint at End Sub so I can look at the variables, I find that vsoCell has the correct value, but shpHgt does not, and neither does intI. I can't for the life of me figure why shpHgt = vsoCell
doesn't actually set the value of vsoCell into the shpHgt variable. Likewise, intI likes to default to "6".
So this is a learning process, and I don't believe I have ever done anything object-oriented in my life so this is new to me as well.
vsoCell is an object. Whilst VBA often converts correctly, in this case it seems to not having been able to.
But you don't need to introduce this intermediate variable, you can get directly the cell value as shown below.
Sub AddCnPts()
'
' Automatically Add Connection Points
'
Dim UndoScopeID2 As Long
Dim intRowIndex1 As Integer
Dim vsoRow1 As Visio.Row
'Dim vsoCell As Visio.Cell *** not needed
Dim vsoShape As Visio.Shape
Dim strHRow As String
Dim shpHgt As Long
Dim intI As Integer
UndoScopeID2 = Application.BeginUndoScope("Add Connect")
intRowIndex1 = ActiveWindow.Selection(1).AddRow(visSectionConnectionPts, visRowLast, visTagCnnctPt)
If ActiveWindow.Selection.Count <> 1 Then
MsgBox "This macro works with exactly ONE shape. Please select one and run again.", vbOK
Exit Sub
End If
Set vsoShape = ActiveWindow.Selection(1)
' Set vsoCell = vsoShape.CellsU("Height")
' shpHgt = vsoCell
shpHgt = vsoShape.Cells("Height").ResultIU
If shpHgt >= 0.5 Then
intI = (shpHgt - 0.25) / 0.125 'Sets number of needed connection points based on geometry of shape
End If
strHRow = "=IF(Height*1>=0.5,Height*1-(0.125*" & intI & "),0)"
Set vsoRow1 = vsoShape.Section(visSectionConnectionPts).Row(intRowIndex1)
With vsoRow1
.Cell(visCnnctX).FormulaU = "Width*1"
.Cell(visCnnctY).FormulaU = strHRow
.Cell(visCnnctDirX).FormulaU = -1#
.Cell(visCnnctDirY).FormulaU = 0#
.Cell(visCnnctType).FormulaU = visCnnctTypeInward
End With
Application.EndUndoScope UndoScopeID2, True
End Sub
Thanks for the feedback. I'm still seeing the same behavior, though.
intI = 0
shpHgt = 0
shpHgt = vsoShape.Cells("Height").ResultIU
If shpHgt >= 0.5 Then
intI = (shpHgt - 0.25) / 0.125 'Sets number of needed connection points based on geometry of shape
End If
If I put a breakpoint at the shpHgt = vsoShape.Cells("Height").ResultIU line, it shows intI and shpHgt as both = 0, and .ResultIU returns the correct value.
However a breakpoint at the beginning of the If statement returns shpHgt = 1, and a breakpoint at End If shows intI = 6.
Both variables are Long at this point. So confused!
Based on this MSDN link (https://msdn.microsoft.com/en-us/VBA/Visio-VBA/articles/cell-resultiu-property-visio), I changed the shpHgt variable to Double, and now both it and the intI are working. How odd that changing the type of one variable affects the performance of another.
I got it working!!!!
Sorry for the multiple posts today.
Here's my 'completed' code. I'm sure I can optimize it further.
Sub AddConnPts()
Dim I As Integer 'Incrementer
Dim N As Integer 'Number of Connection Points Needed
Dim C As Integer 'Number of existing Connection Points
Dim H As Double 'Height of Shape
Dim intRowIndex1 As Integer 'Row Incrementer
Dim WString As String 'String to put in Connect points X Column
Dim HString As String 'String to put in Connect points Y Column
Dim vsoShape As Visio.Shape
Dim vsoRow1 As Visio.Row
If ActiveWindow.Selection.Count <> 1 Then
MsgBox "This macro works with exactly ONE shape. Please select one and run again.", vbOK
Exit Sub
End If
Set vsoShape = ActiveWindow.Selection(1)
H = vsoShape.Cells("Height").ResultIU
If H >= 0.5 Then
N = ((H - 0.25) / 0.125) 'Sets number of needed connection points based on geometry of shape
'Get Existing Connection Points = C
C = ActiveWindow.Selection(1).RowCount(Visio.visSectionConnectionPts)
'Loop through all the rows
For intRowIndex1 = 0 To C - 1
Next intRowIndex1
If C < N Then
For I = C + 1 To N
intRowIndex1 = ActiveWindow.Selection(1).AddRow(visSectionConnectionPts, visRowLast, visTagCnnctPt)
WString = "Width*1"
HString = "IF(Height*1> 0.4375 + 0.125*" & intRowIndex1 - 1 & " ,Height*1-(0.125*" & I & "),0)"
Set vsoRow1 = ActiveWindow.Selection(1).Section(visSectionConnectionPts).Row(intRowIndex1)
vsoRow1.Cell(visCnnctX).FormulaU = WString
vsoRow1.Cell(visCnnctY).FormulaU = HString
vsoRow1.Cell(visCnnctDirX).FormulaU = -1#
vsoRow1.Cell(visCnnctDirY).FormulaU = 0#
vsoRow1.Cell(visCnnctType).FormulaU = visCnnctTypeInward
Next I
End If
End If
End Sub
I've played with it a bit more and have a final solution that works with all of my desired connector shapes. I've attached a VSD file with them, and the 'final' code below. I'll edit the thread also to indicate that this issue is solved. Thanks for all the help!
Sub AddConnPts()
Dim I As Integer 'Incrementer
Dim N As Integer 'Number of Connection Points Needed
Dim C As Integer 'Number of existing Connection Points
Dim H As Double 'Height of Shape
Dim G As Double 'Geometry constant from Scratch section
Dim intRowIndex1 As Integer 'Row Incrementer
Dim WString As String 'String to put in Connect points X Column
Dim HString As String 'String to put in Connect points Y Column
Dim vsoShape As Visio.Shape
Dim vsoRow1 As Visio.Row
If ActiveWindow.Selection.Count <> 1 Then
MsgBox "This macro works with exactly ONE shape. Please select one and run again.", vbOK
Exit Sub
End If
Set vsoShape = ActiveWindow.Selection(1)
H = vsoShape.Cells("Height").ResultIU
G = vsoShape.Cells("Scratch.Y1").ResultIU
'Check to see if the shape hieght meets miminum for two connection points
If H >= G Then
'Sets number of needed connection points based on geometry of shape
'Subtract G from the total height
'G is 4.5 grid spaces (0.0625) for Broken Connector
'G is 2.5 grid spaces (0.0625) for Whole Connector
G = vsoShape.Cells("Scratch.A1").ResultIU
N = ((H - G * 0.0625) / 0.125)
'Get Existing Connection Points = C
C = ActiveWindow.Selection(1).RowCount(Visio.visSectionConnectionPts)
'Loop through all the rows
For intRowIndex1 = 0 To C - 1
Next intRowIndex1
If C < N Then
For I = C + 1 To N
intRowIndex1 = ActiveWindow.Selection(1).AddRow(visSectionConnectionPts, visRowLast, visTagCnnctPt)
WString = "Width*1"
'Used for the Height Dimension of the connector to place the connection points
G = vsoShape.Cells("Scratch.X1").ResultIU
HString = "IF(Height*1> " & G & " + 0.125*" & intRowIndex1 - 1 & " ,Height*1-(0.125*" & I & "),0)"
Set vsoRow1 = ActiveWindow.Selection(1).Section(visSectionConnectionPts).Row(intRowIndex1)
vsoRow1.Cell(visCnnctX).FormulaU = WString
vsoRow1.Cell(visCnnctY).FormulaU = HString
vsoRow1.Cell(visCnnctDirX).FormulaU = -1#
vsoRow1.Cell(visCnnctDirY).FormulaU = 0#
vsoRow1.Cell(visCnnctType).FormulaU = visCnnctTypeInward
Next I
End If
End If
End Sub