Visio Proximity Movement

Started by Abeiis, April 25, 2017, 07:04:56 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Abeiis

Hi ALL,
I need your help (code) to do the following:
I have a diagram with 5 columns, each may have 5 - 10 unconnected Process shapes - focusing on one column, I need to move a shape say in the 6th positon to 3rd position. doing so, the shapes that were in the 3,4,5 would move downward giving room to the selected shape to be placed in the 3rd position. Likewise, moving the 2nd position shape to the 5th, shapes 5,4,3 would move upwards.
My thought to do this, once a shape is selected to move, its position is logged and as its current position in the motion changes, if upward motion detected, the closest shape in the column would move downward, and so on. Please help, Thanks!

vojo

IMHO...way too complicated...better off doing the rearrangement by hand
(<30 seconds each time manually vs a month getting code to work and then support it)
(in essence, you want recreate shape dragging and add text).

Could certainly do it in code, but so many combinations
(shape 1 to 5th position, 2 to 3, 4 to 2, etc...then same for col 1,2,3,4,5).

Might be better off using an excel workbook
- 5 columns for 5 columns
- row 1 = shape type
- row 2 = text in shape
code would clear drawing, pull from excel, and add shapes, populate, and align.
For changes, update workbook and rerun code.  That would be more manageable

I think you will find that this will be a lot of work for limited return



Abeiis

Thank you for your reply - it is very important to do it in Visio (leadership request).
I think if you review my suggestion, we can divide and conquer any difficulties.
1. I need a code that; focusing on one column via X value, would check the selected shape-A, Y value as I move it.
2. If the Y value is > the shape-B above it, Shape-B would take its place (I know the X,Y of shape-A).
3. Reverse the logic when moving the other direction.
4. set the code in a loop until shape-A is deselected.
Is that doable, Thanks!

wapperdude

Yes, do-able. But not easy...needs events.

The basic problem is that none of the shapes know anything about each other.  Their "isolation" is even stronger because they are not connected.  I think my approach would be to loop thru all the shapes on the page and store them in a multi-dimensional array.  That way, they can be organised by column (x-pos), and then by y-pos.  Then, based upon selected shape x-, y-position you can determine the column and all shapes in it, and also who the neighbors are.  That's all pretty easy.

The tough part, as I see it,is moving the shape to a new location.  There must be an event(s) to trigger this, and it must be a unique event that is associated with the shape in question.  Perhaps mouse down / up events are adequate???  A variety of decisions must be made:

1st event:  shape vacates existing spot.  Is the array updated, filling in the vacated spot by moving all shapes below upward, or some other code-able scheme?

2nd event:  shape placed in new location.  Let's say mouse up signals final resting spot (for the newly departed  ::) ).  Now add shape back into the array, moving down shapes that will be below it.  Might require some temporary "bookkeeping" arrays for keeping track of things.

Something to think about as a possible methodology.

HTH
Wapperdude
Visio 2019 Pro

wapperdude

#5
Fleshing out a possible approach...
1) User starts code to re-arrange shapes
      a) loop thru all shapes on page and enter into an array: index A = shape Name, index B = xpos, index C = ypos
      b) resort array to neatly arrange shapes by x & y values

2) Code asks user to select shape to be moved
      a) mouse down event starts desired sub routine to extract selected shape and fill it's vacancy.
      b) drag shape to desired new location
      c) mouse up event starts final shape re-arranging.  Note, there may be some timing issues.  This code action must wait for previous code action to complete.

I'm sure there are other ways to approach this, and there's no guarantee that all of the suggested steps can be performed as outlined. 

Wapperdude
Visio 2019 Pro

Abeiis

Thank you for your reply,
Great approach - I agree one needs to use Events, as stated, the imitation/trigger is the selection of a shape and moving it. Using an array is good, ,but I thought one can build a code that focus only on one column by logging the selected shape's X value then build the array for all shapes with that value.
I am a novice in this area and ask for your help coding this loop, Thanks!

wapperdude

#7
Yes, you could force the code to do a single column...for example, use "IF" statement to find shapes that match X- value of selected shape.  Perfectly fine approach.  Probably easier to implement since there's fewer things to track.

The loop would go thru all shapes on the page, compare PinX value to PinX of selected shape.  If successful, put into array.

Wapperdude

Visio 2019 Pro

Abeiis

I understand, but the code is what I need help with ;-)

wapperdude

Good way to start is to download the SDK for your version of Visio.  Contains much info and examples.  Also, the macro recorder is very useful for specific commands.  Of course it won't give you things like loops, if statements.  Also, Google something like Visio vba some task, here some task might be find shapes on a page, or for next loop, or get shape position on a page.

Wapperdude
Visio 2019 Pro

wapperdude

#10
Since sorting the array is not built into VBA, it would take some code development.  I found the following code.  As provided,there are two pieces:  a sub, and a function.  The sub would be replaced by your code development.  But your code needs to include the function call.

In the example sub, a 1-dimensional array was hard coded.  You will have 2-dimensional array (shape Name, yLoc).  The function needs to be modified for 2-dimensional array.  Sorting will be based upon yLoc values.

Wapperdude

Option Explicit
Sub SortArray()
' hiker95, 03/11/2013
' http://www.mrexcel.com/forum/excel-questions/690718-visual-basic-applications-sort-array-numbers.html
Dim MyArray As Variant, i As Long
MyArray = Array(1, 4, 2, 32, 5, 21)
' True being sort as Ascending. False will sort Decending.
MyArray = BubbleSrt(MyArray, True)
For i = LBound(MyArray) To UBound(MyArray)
  MsgBox MyArray(i)
Next i
End Sub

Public Function BubbleSrt(ArrayIn, Ascending As Boolean)
' rjwebgraphix, 03/11/2013
' http://www.mrexcel.com/forum/excel-questions/690718-visual-basic-applications-sort-array-numbers.html
' True being sort as Ascending. False will sort Decending.
Dim SrtTemp As Variant
Dim i As Long
Dim j As Long
If Ascending = True Then
  For i = LBound(ArrayIn) To UBound(ArrayIn)
    For j = i + 1 To UBound(ArrayIn)
      If ArrayIn(i) > ArrayIn(j) Then
        SrtTemp = ArrayIn(j)
        ArrayIn(j) = ArrayIn(i)
        ArrayIn(i) = SrtTemp
      End If
    Next j
  Next i
Else
  For i = LBound(ArrayIn) To UBound(ArrayIn)
    For j = i + 1 To UBound(ArrayIn)
      If ArrayIn(i) < ArrayIn(j) Then
        SrtTemp = ArrayIn(j)
        ArrayIn(j) = ArrayIn(i)
        ArrayIn(i) = SrtTemp
      End If
    Next j
   Next i
End If
BubbleSrt = ArrayIn
End Function
Visio 2019 Pro

wapperdude

#11
Will you be the only one doing this?

There is a pretty simple method that doesn't require coding...well, it could be coded.  The core to the solution is the distribution shapes function.  Evenly distributes shapes between 1st and last shapes.  Optimum for uniformity:  1st and last shapes placed in their final locations vertically.

Methodology.  Select and move shape to desired approximate location vertically...somewhere between two shapes.  Location does not have to be precise.  Next, select all shapes in the column.  Finally use the distribution function to evenly space the interior shapes between the 1st and last shapes.  That's it.  Quick and simple.

Wapperdude
Visio 2019 Pro

wapperdude

#12
OK.  This was pretty quick to code up.


Sub Macro1()

    Dim vShp As Shape
    Dim pShp As Shape
    Dim Xmin As Double, Xmax As Double  'Optional: sets horiz limits for shapes to be in same column

'Select a shape to be moved and place it...note, should add check to verify a shape has been selected.
'or use event to fire macro, in which case the shape is selected and no further check ended.
    Set vShp = ActiveWindow.Selection(1)
    Xmin = vShp.CellsU("PinX").Result("in") - 1 'Limits are tentatively based upon selected shape position.  Not optimal.
    Xmax = vShp.CellsU("PinX").Result("in") + 1
   
    ActiveWindow.DeselectAll

'Select shapes in the column:
    For Each pShp In ActivePage.Shapes
        If pShp.CellsU("PinX").Result("in") > Xmin And pShp.CellsU("PinX").Result("in") < Xmax Then
            ActiveWindow.Select pShp, visSelect
        End If
    Next
   
    ActiveWindow.Selection.Distribute visDistVertMiddle, False

End Sub


See attached for example.  I colored the shapes of interest in yellow.  Select one of these shapes.  In the example, Process.6 was selected and moved.  Just re-select, then run the macro.

For details about "distribute":  https://msdn.microsoft.com/en-us/library/office/ff766983.aspx

HTH
Wapperdude
Visio 2019 Pro

Abeiis

Hi Wapperdude,
Waw... your input and help is greatly appreciated and I will try your last suggestion and post my results... Thanks again for all your help :-) :)

Abeiis

Hi Wapperdude,
The last code to distribute worked but because I have other shapes within the x axis (i.e. diagram block, Legends...) I decided to move all my process shapes in a separate layer.
I need to ask you 2 things:
1. How to force the code to run in a specific layer
2. How to resort the newly distributed shapes IDs correspond to the new location - meaning: when I move shape ID-3 top position 5 it will reorder to ID-5 and ID-4 will be 3 and ID-5 will be 4.

Thanks!